summaryrefslogtreecommitdiffstats
path: root/src/util
diff options
context:
space:
mode:
authorMenTaLguY <mental@rydia.net>2006-01-16 02:36:01 +0000
committermental <mental@users.sourceforge.net>2006-01-16 02:36:01 +0000
commit179fa413b047bede6e32109e2ce82437c5fb8d34 (patch)
treea5a6ac2c1708bd02288fbd8edb2ff500ff2e0916 /src/util
downloadinkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.tar.gz
inkscape-179fa413b047bede6e32109e2ce82437c5fb8d34.zip
moving trunk for module inkscape
(bzr r1)
Diffstat (limited to 'src/util')
-rw-r--r--src/util/.cvsignore5
-rw-r--r--src/util/Makefile_insert25
-rw-r--r--src/util/compose.hpp393
-rw-r--r--src/util/filter-list.h65
-rw-r--r--src/util/forward-pointer-iterator.h120
-rw-r--r--src/util/glib-list-iterators.h238
-rw-r--r--src/util/list-container-test.cpp204
-rw-r--r--src/util/list-container.h352
-rw-r--r--src/util/list.h407
-rw-r--r--src/util/makefile.in17
-rw-r--r--src/util/map-list.h68
-rw-r--r--src/util/reverse-list.h68
-rw-r--r--src/util/shared-c-string-ptr.cpp50
-rw-r--r--src/util/shared-c-string-ptr.h77
-rw-r--r--src/util/tuple.h182
-rw-r--r--src/util/ucompose.hpp438
-rw-r--r--src/util/units.cpp344
-rw-r--r--src/util/units.h88
18 files changed, 3141 insertions, 0 deletions
diff --git a/src/util/.cvsignore b/src/util/.cvsignore
new file mode 100644
index 000000000..e0bbf26ba
--- /dev/null
+++ b/src/util/.cvsignore
@@ -0,0 +1,5 @@
+.deps
+.dirstamp
+.libs
+makefile
+list-container-test
diff --git a/src/util/Makefile_insert b/src/util/Makefile_insert
new file mode 100644
index 000000000..1b7119180
--- /dev/null
+++ b/src/util/Makefile_insert
@@ -0,0 +1,25 @@
+
+util/all: util/libinkutil.a
+
+util/clean:
+ rm -f util/libinkutil.a $(util_libinkutil_a_OBJECTS)
+
+util_libinkutil_a_SOURCES = \
+ util/compose.hpp \
+ util/ucompose.hpp \
+ util/filter-list.h \
+ util/forward-pointer-iterator.h \
+ util/glib-list-iterators.h \
+ util/list.h \
+ util/list-container.h \
+ util/map-list.h \
+ util/reverse-list.h \
+ util/shared-c-string-ptr.h \
+ util/shared-c-string-ptr.cpp \
+ util/tuple.h \
+ util/units.cpp \
+ util/units.h
+
+util_list_container_test_SOURCES = util/list-container-test.cpp
+util_list_container_test_LDADD = gc.o $(INKSCAPE_LIBS)
+
diff --git a/src/util/compose.hpp b/src/util/compose.hpp
new file mode 100644
index 000000000..b3f410c8e
--- /dev/null
+++ b/src/util/compose.hpp
@@ -0,0 +1,393 @@
+/* Defines String::compose(fmt, arg...) for easy, i18n-friendly
+ * composition of strings.
+ *
+ * Version 1.0.
+ *
+ * Copyright (c) 2002 Ole Laursen <olau@hardworking.dk>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+//
+// Basic usage is like
+//
+// std::cout << String::compose("This is a %1x%2 matrix.", rows, cols);
+//
+// See http://www.cs.aau.dk/~olau/compose/ or the included README.compose for
+// more details.
+//
+
+#ifndef STRING_COMPOSE_H
+#define STRING_COMPOSE_H
+
+#include <sstream>
+#include <string>
+#include <list>
+#include <map> // for multimap
+
+namespace StringPrivate
+{
+ // the actual composition class - using string::compose is cleaner, so we
+ // hide it here
+ class Composition
+ {
+ public:
+ // initialize and prepare format string on the form "text %1 text %2 etc."
+ explicit Composition(std::string fmt);
+
+ // supply an replacement argument starting from %1
+ template <typename T>
+ Composition &arg(const T &obj);
+
+ // compose and return string
+ std::string str() const;
+
+ private:
+ std::ostringstream os;
+ int arg_no;
+
+ // we store the output as a list - when the output string is requested, the
+ // list is concatenated to a string; this way we can keep iterators into
+ // the list instead of into a string where they're possibly invalidated on
+ // inserting a specification string
+ typedef std::list<std::string> output_list;
+ output_list output;
+
+ // the initial parse of the format string fills in the specification map
+ // with positions for each of the various %?s
+ typedef std::multimap<int, output_list::iterator> specification_map;
+ specification_map specs;
+ };
+
+ // helper for converting spec string numbers
+ inline int char_to_int(char c)
+ {
+ switch (c) {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ default: return -1000;
+ }
+ }
+
+ inline bool is_number(int n)
+ {
+ switch (n) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+
+ // implementation of class Composition
+ template <typename T>
+ inline Composition &Composition::arg(const T &obj)
+ {
+ os << obj;
+
+ std::string rep = os.str();
+
+ if (!rep.empty()) { // manipulators don't produce output
+ for (specification_map::const_iterator i = specs.lower_bound(arg_no),
+ end = specs.upper_bound(arg_no); i != end; ++i) {
+ output_list::iterator pos = i->second;
+ ++pos;
+
+ output.insert(pos, rep);
+ }
+
+ os.str(std::string());
+ //os.clear();
+ ++arg_no;
+ }
+
+ return *this;
+ }
+
+ inline Composition::Composition(std::string fmt)
+ : arg_no(1)
+ {
+ std::string::size_type b = 0, i = 0;
+
+ // fill in output with the strings between the %1 %2 %3 etc. and
+ // fill in specs with the positions
+ while (i < fmt.length()) {
+ if (fmt[i] == '%' && i + 1 < fmt.length()) {
+ if (fmt[i + 1] == '%') { // catch %%
+ fmt.replace(i, 2, "%");
+ ++i;
+ }
+ else if (is_number(fmt[i + 1])) { // aha! a spec!
+ // save string
+ output.push_back(fmt.substr(b, i - b));
+
+ int n = 1; // number of digits
+ int spec_no = 0;
+
+ do {
+ spec_no += char_to_int(fmt[i + n]);
+ spec_no *= 10;
+ ++n;
+ } while (i + n < fmt.length() && is_number(fmt[i + n]));
+
+ spec_no /= 10;
+ output_list::iterator pos = output.end();
+ --pos; // safe since we have just inserted a string>
+
+ specs.insert(specification_map::value_type(spec_no, pos));
+
+ // jump over spec string
+ i += n;
+ b = i;
+ }
+ else
+ ++i;
+ }
+ else
+ ++i;
+ }
+
+ if (i - b > 0) // add the rest of the string
+ output.push_back(fmt.substr(b, i - b));
+ }
+
+ inline std::string Composition::str() const
+ {
+ // assemble string
+ std::string str;
+
+ for (output_list::const_iterator i = output.begin(), end = output.end();
+ i != end; ++i)
+ str += *i;
+
+ return str;
+ }
+}
+
+// now for the real thing(s)
+namespace String
+{
+ // a series of functions which accept a format string on the form "text %1
+ // more %2 less %3" and a number of templated parameters and spits out the
+ // composited string
+ template <typename T1>
+ inline std::string compose(const std::string &fmt, const T1 &o1)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1);
+ return c.str();
+ }
+
+ template <typename T1, typename T2>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13, const T14 &o14)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14,
+ typename T15>
+ inline std::string compose(const std::string &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13, const T14 &o14, const T15 &o15)
+ {
+ StringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
+ return c.str();
+ }
+}
+
+
+#endif // STRING_COMPOSE_H
diff --git a/src/util/filter-list.h b/src/util/filter-list.h
new file mode 100644
index 000000000..e00c33b08
--- /dev/null
+++ b/src/util/filter-list.h
@@ -0,0 +1,65 @@
+/*
+ * Inkscape::Util::filter_list - select a subset of the items in a list
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_UTIL_FILTER_LIST_H
+#define SEEN_INKSCAPE_UTIL_FILTER_LIST_H
+
+#include "util/list.h"
+#include "traits/list-copy.h"
+
+namespace Inkscape {
+
+namespace Util {
+
+template <typename InputIterator, typename UnaryPredicate>
+inline typename Traits::ListCopy<InputIterator>::ResultList
+filter_list(UnaryPredicate p, InputIterator start, InputIterator end) {
+ typedef typename Traits::ListCopy<InputIterator>::ResultList ResultList;
+ ResultList head;
+ ResultList tail;
+ while ( start != end && !p(*start) ) {
+ ++start;
+ }
+ if ( start != end ) {
+ head = tail = ResultList(*start);
+ ++start;
+ }
+ while ( start != end ) {
+ if (p(*start)) {
+ set_rest(tail, ResultList(*start));
+ ++tail;
+ }
+ ++start;
+ }
+ return head;
+}
+
+template <typename T, typename UnaryPredicate>
+inline typename Traits::ListCopy<List<T> >::ResultList
+filter_list(UnaryPredicate p, List<T> const &list) {
+ return filter_list(p, list, List<T>());
+}
+
+}
+
+}
+
+#endif
+/*
+ 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 :
diff --git a/src/util/forward-pointer-iterator.h b/src/util/forward-pointer-iterator.h
new file mode 100644
index 000000000..2c2345c81
--- /dev/null
+++ b/src/util/forward-pointer-iterator.h
@@ -0,0 +1,120 @@
+/*
+ * Inkscape::Util::ForwardPointerIterator - wraps a simple pointer
+ * with various strategies
+ * to determine sequence
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_UTIL_FORWARD_POINTER_ITERATOR_H
+#define SEEN_INKSCAPE_UTIL_FORWARD_POINTER_ITERATOR_H
+
+#include <iterator>
+#include "traits/reference.h"
+
+namespace Inkscape {
+
+namespace Util {
+
+template <typename BaseType, typename Strategy>
+class ForwardPointerIterator;
+
+template <typename BaseType, typename Strategy>
+class ForwardPointerIterator<BaseType const, Strategy> {
+public:
+ typedef std::forward_iterator_tag iterator_category;
+ typedef typename Traits::Reference<BaseType const>::LValue value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef typename Traits::Reference<BaseType const>::LValue reference;
+ typedef typename Traits::Reference<BaseType const>::RValue const_reference;
+ typedef typename Traits::Reference<BaseType const>::Pointer pointer;
+
+ typedef ForwardPointerIterator<BaseType const, Strategy> Self;
+
+ ForwardPointerIterator() : _p(NULL) {}
+ ForwardPointerIterator(pointer p) : _p(p) {}
+
+ operator pointer() const { return _p; }
+ reference operator*() const { return *_p; }
+ pointer operator->() const { return _p; }
+
+ bool operator==(Self const &other) const {
+ return _p == other._p;
+ }
+ bool operator!=(Self const &other) const {
+ return _p != other._p;
+ }
+
+ Self &operator++() {
+ _p = Strategy::next(_p);
+ return *this;
+ }
+ Self operator++(int) {
+ Self old(*this);
+ operator++();
+ return old;
+ }
+
+ operator bool() const { return _p != NULL; }
+
+private:
+ pointer _p;
+};
+
+template <typename BaseType, typename Strategy>
+class ForwardPointerIterator
+: public ForwardPointerIterator<BaseType const, Strategy>
+{
+public:
+ typedef typename Traits::Reference<BaseType>::LValue value_type;
+ typedef typename Traits::Reference<BaseType>::LValue reference;
+ typedef typename Traits::Reference<BaseType>::RValue const_reference;
+ typedef typename Traits::Reference<BaseType>::Pointer pointer;
+
+ typedef ForwardPointerIterator<BaseType const, Strategy> Ancestor;
+ typedef ForwardPointerIterator<BaseType, Strategy> Self;
+
+ ForwardPointerIterator() : Ancestor() {}
+ ForwardPointerIterator(pointer p) : Ancestor(p) {}
+
+ operator pointer() const {
+ return const_cast<pointer>(Ancestor::operator->());
+ }
+ reference operator*() const {
+ return const_cast<reference>(Ancestor::operator*());
+ }
+ pointer operator->() const {
+ return const_cast<pointer>(Ancestor::operator->());
+ }
+
+ Self &operator++() {
+ Ancestor::operator++();
+ return *this;
+ }
+ Self operator++(int) {
+ Self old(*this);
+ operator++();
+ return old;
+ }
+};
+
+}
+
+}
+
+#endif
+/*
+ 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 :
diff --git a/src/util/glib-list-iterators.h b/src/util/glib-list-iterators.h
new file mode 100644
index 000000000..586bc314a
--- /dev/null
+++ b/src/util/glib-list-iterators.h
@@ -0,0 +1,238 @@
+/*
+ * Inkscape::Util::GSListIterator - STL iterator for GSList
+ * Inkscape::Util::GSListConstIterator - STL iterator for GSList
+ * Inkscape::Util::GListIterator - STL iterator for GList
+ * Inkscape::Util::GListConstIterator - STL iterator for GList
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2005 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_GLIB_LIST_ITERATORS_H
+#define SEEN_INKSCAPE_GLIB_LIST_ITERATORS_H
+
+#include <cstddef>
+#include <iterator>
+#include "glib/gslist.h"
+#include "glib/glist.h"
+
+namespace Inkscape {
+
+namespace Util {
+
+template <typename T> class GSListConstIterator;
+template <typename T> class GSListIterator;
+template <typename T> class GListConstIterator;
+template <typename T> class GListIterator;
+
+template <typename T>
+class GSListConstIterator<T *> {
+public:
+ typedef std::forward_iterator_tag iterator_category;
+ typedef T * const value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef value_type *pointer;
+ typedef value_type &reference;
+
+ GSListConstIterator(GSList const *list) : _list(list) {}
+ // default copy
+ // default assign
+ GSList const *list() const { return _list; }
+
+ reference operator*() const {
+ return *reinterpret_cast<pointer>(&_list->data);
+ }
+
+ bool operator==(GSListConstIterator const &other) {
+ return other._list == _list;
+ }
+ bool operator!=(GSListConstIterator const &other) {
+ return other._list != _list;
+ }
+
+ GSListConstIterator &operator++() {
+ _list = _list->next;
+ return *this;
+ }
+ GSListConstIterator operator++(int) {
+ GSListConstIterator saved=*this;
+ _list = _list->next;
+ return saved;
+ }
+
+private:
+ GSList const *_list;
+};
+
+template <typename T>
+class GSListIterator<T *> {
+public:
+ typedef std::forward_iterator_tag iterator_category;
+ typedef T *value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef value_type *pointer;
+ typedef value_type &reference;
+ typedef value_type const &const_reference;
+
+ GSListIterator(GSList *list) : _list(list) {}
+ // default copy
+ // default assign
+ operator GSListConstIterator<T *>() const { return _list; }
+ GSList const *list() const { return _list; }
+ GSList *list() { return _list; }
+
+ const_reference operator*() const {
+ return *reinterpret_cast<pointer>(&_list->data);
+ }
+ reference operator*() {
+ return *reinterpret_cast<pointer>(&_list->data);
+ }
+
+ bool operator==(GSListIterator const &other) {
+ return other._list == _list;
+ }
+ bool operator!=(GSListIterator const &other) {
+ return other._list != _list;
+ }
+
+ GSListIterator &operator++() {
+ _list = _list->next;
+ return *this;
+ }
+ GSListIterator operator++(int) {
+ GSListIterator saved=*this;
+ _list = _list->next;
+ return saved;
+ }
+
+private:
+ GSList *_list;
+};
+
+template <typename T>
+class GListConstIterator<T *> {
+public:
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef T * const value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef value_type *pointer;
+ typedef value_type &reference;
+
+ GListConstIterator(GList const *list) : _list(list) {}
+ // default copy
+ // default assign
+ GList const *list() const { return _list; }
+
+ reference operator*() const {
+ return *reinterpret_cast<pointer>(&_list->data);
+ }
+
+ bool operator==(GListConstIterator const &other) {
+ return other._list == _list;
+ }
+ bool operator!=(GListConstIterator const &other) {
+ return other._list != _list;
+ }
+
+ GListConstIterator &operator++() {
+ _list = _list->next;
+ return *this;
+ }
+ GListConstIterator operator++(int) {
+ GListConstIterator saved=*this;
+ _list = _list->next;
+ return saved;
+ }
+
+ GListConstIterator &operator--() {
+ _list = _list->prev;
+ return *this;
+ }
+ GListConstIterator operator--(int) {
+ GListConstIterator saved=*this;
+ _list = _list->prev;
+ return saved;
+ }
+
+private:
+ GList const *_list;
+};
+
+template <typename T>
+class GListIterator<T *> {
+public:
+ typedef std::bidirectional_iterator_tag iterator_category;
+ typedef T *value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef value_type *pointer;
+ typedef value_type &reference;
+ typedef value_type const &const_reference;
+
+ GListIterator(GList *list) : _list(list) {}
+ // default copy
+ // default assign
+ operator GSListConstIterator<T *>() const {
+ return reinterpret_cast<GSList *>(_list);
+ }
+ operator GSListIterator<T *>() const {
+ return reinterpret_cast<GSList *>(_list);
+ }
+ operator GListConstIterator<T *>() const { return _list; }
+ GList const *list() const { return _list; }
+ GList *list() { return _list; }
+
+ const_reference operator*() const {
+ return *reinterpret_cast<pointer>(&_list->data);
+ }
+ reference operator*() { return *reinterpret_cast<pointer>(&_list->data); }
+
+ bool operator==(GListIterator const &other) {
+ return other._list == _list;
+ }
+ bool operator!=(GListIterator const &other) {
+ return other._list != _list;
+ }
+
+ GListIterator &operator++() {
+ _list = _list->next;
+ return *this;
+ }
+ GListIterator operator++(int) {
+ GListIterator saved=*this;
+ _list = _list->next;
+ return saved;
+ }
+
+ GListIterator &operator--() {
+ _list = _list->prev;
+ return *this;
+ }
+ GListIterator operator--(int) {
+ GListIterator saved=*this;
+ _list = _list->prev;
+ return saved;
+ }
+
+private:
+ GList *_list;
+};
+
+}
+
+}
+
+#endif
+/*
+ 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 :
diff --git a/src/util/list-container-test.cpp b/src/util/list-container-test.cpp
new file mode 100644
index 000000000..e309677c0
--- /dev/null
+++ b/src/util/list-container-test.cpp
@@ -0,0 +1,204 @@
+#include <stdarg.h>
+#include "../utest/utest.h"
+#include "util/list-container.h"
+
+using Inkscape::Util::ListContainer;
+
+#define ARRAY_RANGE(array) (array), (array)+sizeof((array))/sizeof((array)[0])
+
+static bool check_values(ListContainer<int> const &c, unsigned n_values, ...) {
+ bool ret = true;
+ va_list args;
+ va_start(args, n_values);
+ ListContainer<int>::const_iterator iter(c.begin());
+ while ( n_values && iter != c.end() ) {
+ int const value = va_arg(args, int);
+ if ( value != *iter ) {
+ ret = false;
+ }
+ if ( n_values == 1 && &c.back() != &*iter ) {
+ ret = false;
+ }
+ n_values--;
+ ++iter;
+ }
+ va_end(args);
+ return ret && n_values == 0 && iter == c.end();
+}
+
+int main(int argc, char *argv[]) {
+ Inkscape::GC::init();
+ utest_start("List Container");
+ UTEST_TEST("range constructor") {
+ int const values[]={1,2,3,4};
+ int const * const values_end=values+4;
+ ListContainer<int> container(values, values_end);
+
+ ListContainer<int>::iterator container_iter=container.begin();
+ int const * values_iter=values;
+
+ while ( values_iter != values_end && container_iter != container.end() ) {
+ UTEST_ASSERT(*values_iter == *container_iter);
+ ++values_iter;
+ ++container_iter;
+ }
+
+ UTEST_ASSERT(values_iter == values_end);
+ UTEST_ASSERT(container_iter == container.end());
+ }
+ UTEST_TEST("equality tests") {
+ int const a[] = { 1, 2, 3, 4 };
+ int const b[] = { 1, 2, 3, 4 };
+ int const c[] = { 1, 2, 3 };
+ int const d[] = { 1, 2, 3, 5 };
+ ListContainer<int> c_a(ARRAY_RANGE(a));
+ ListContainer<int> c_b(ARRAY_RANGE(b));
+ ListContainer<int> c_c(ARRAY_RANGE(c));
+ ListContainer<int> c_d(ARRAY_RANGE(d));
+
+ UTEST_ASSERT(c_a == c_b);
+ UTEST_ASSERT(!( c_a != c_b ));
+ UTEST_ASSERT(!( c_a == c_c ));
+ UTEST_ASSERT(c_a != c_c);
+ UTEST_ASSERT(!( c_a == c_d ));
+ UTEST_ASSERT(c_a != c_d);
+ }
+ UTEST_TEST("lessthan tests") {
+ int const a[] = { 1, 2, 3, 4 };
+ int const b[] = { 1, 2, 2, 4 };
+ int const c[] = { 1, 2, 4, 4 };
+ int const d[] = { 1, 2, 3 };
+ ListContainer<int> c_a(ARRAY_RANGE(a));
+ ListContainer<int> c_b(ARRAY_RANGE(b));
+ ListContainer<int> c_c(ARRAY_RANGE(c));
+ ListContainer<int> c_d(ARRAY_RANGE(d));
+ UTEST_ASSERT(c_a >= c_b);
+ UTEST_ASSERT(!( c_a < c_b ));
+ UTEST_ASSERT(!( c_a >= c_c ));
+ UTEST_ASSERT(c_a < c_c);
+ UTEST_ASSERT(!( c_a < c_d ));
+ UTEST_ASSERT(c_a >= c_d);
+ UTEST_ASSERT(c_d < c_a);
+ }
+ UTEST_TEST("assignment operator") {
+ int const a[] = { 1, 2, 3, 4 };
+ ListContainer<int> c_a(ARRAY_RANGE(a));
+ ListContainer<int> c_c;
+ UTEST_ASSERT(c_a != c_c);
+ c_c = c_a;
+ UTEST_ASSERT(c_a == c_c);
+ c_c = c_a;
+ UTEST_ASSERT(c_a == c_c);
+ }
+ UTEST_TEST("fill constructor") {
+ ListContainer<int> filled((std::size_t)3, 2);
+ UTEST_ASSERT(check_values(filled, 3, 2, 2, 2));
+ }
+ UTEST_TEST("container size") {
+ ListContainer<int> empty;
+ UTEST_ASSERT(empty.empty());
+ UTEST_ASSERT(empty.size() == 0);
+ int const a[] = { 1, 2, 3 };
+ ListContainer<int> c_a(ARRAY_RANGE(a));
+ UTEST_ASSERT(!c_a.empty());
+ UTEST_ASSERT(c_a.size() == 3);
+
+ UTEST_ASSERT(empty.max_size() > 0);
+ }
+ UTEST_TEST("appending") {
+ ListContainer<int> c;
+ c.push_back(1);
+ UTEST_ASSERT(check_values(c, 1, 1));
+ c.push_back(2);
+ UTEST_ASSERT(check_values(c, 2, 1, 2));
+ c.push_back(3);
+ UTEST_ASSERT(check_values(c, 3, 1, 2, 3));
+ }
+ UTEST_TEST("bulk appending") {
+ int const a[] = { 1, 2, 3, 4 };
+ int const b[] = { 5, 6, 7 };
+ ListContainer<int> c_a(ARRAY_RANGE(a));
+ ListContainer<int> c_b(ARRAY_RANGE(b));
+ c_a.insert(c_a.end(), c_b.begin(), c_b.end());
+ UTEST_ASSERT(check_values(c_a, 7, 1, 2, 3, 4, 5, 6, 7));
+ }
+ UTEST_TEST("prepending") {
+ ListContainer<int> c;
+ c.push_front(1);
+ UTEST_ASSERT(check_values(c, 1, 1));
+ c.push_front(2);
+ UTEST_ASSERT(check_values(c, 2, 2, 1));
+ c.push_front(3);
+ UTEST_ASSERT(check_values(c, 3, 3, 2, 1));
+ }
+ UTEST_TEST("single-value insertion") {
+ ListContainer<int> c;
+
+ c.insert(c.begin(), 1);
+ UTEST_ASSERT(check_values(c, 1, 1));
+
+ c.insert(c.end(), 2);
+ UTEST_ASSERT(check_values(c, 2, 1, 2));
+
+ c.insert(c.begin(), 3);
+ UTEST_ASSERT(check_values(c, 3, 3, 1, 2));
+
+ ListContainer<int>::iterator pos=c.begin();
+ ++pos;
+ c.insert(pos, 4);
+ UTEST_ASSERT(check_values(c, 4, 3, 4, 1, 2));
+ }
+ UTEST_TEST("single-value erasure") {
+ int const values[] = { 1, 2, 3, 4 };
+ ListContainer<int> c(ARRAY_RANGE(values));
+
+ c.erase(c.begin());
+ UTEST_ASSERT(check_values(c, 3, 2, 3, 4));
+
+ ListContainer<int>::iterator pos=c.begin();
+ ++pos;
+ c.erase(pos);
+ UTEST_ASSERT(check_values(c, 2, 2, 4));
+
+ pos=c.begin();
+ ++pos;
+ c.erase(pos);
+ UTEST_ASSERT(check_values(c, 1, 2));
+
+ c.erase(c.begin());
+ UTEST_ASSERT(check_values(c, 0));
+ }
+ UTEST_TEST("pop_front") {
+ int const full_ary[] = { 1, 2, 3 };
+ ListContainer<int> t(ARRAY_RANGE(full_ary));
+ UTEST_ASSERT(check_values(t, 3, 1, 2, 3));
+ UTEST_ASSERT(t.back() == 3);
+ t.pop_front();
+ UTEST_ASSERT(check_values(t, 2, 2, 3));
+ UTEST_ASSERT(t.back() == 3);
+ t.push_back(23);
+ UTEST_ASSERT(check_values(t, 3, 2, 3, 23));
+ UTEST_ASSERT(t.back() == 23);
+ t.pop_front();
+ UTEST_ASSERT(check_values(t, 2, 3, 23));
+ UTEST_ASSERT(t.back() == 23);
+ t.pop_front();
+ UTEST_ASSERT(check_values(t, 1, 23));
+ UTEST_ASSERT(t.back() == 23);
+ t.pop_front();
+ UTEST_ASSERT(check_values(t, 0));
+ t.push_back(42);
+ UTEST_ASSERT(check_values(t, 1, 42));
+ UTEST_ASSERT(t.back() == 42);
+ }
+ UTEST_TEST("erase_after") {
+ int const full_ary[] = { 1, 2, 3, 4 };
+ int const exp_ary[] = { 1, 3, 4 };
+ ListContainer<int> full_list(ARRAY_RANGE(full_ary));
+ ListContainer<int> exp_list(ARRAY_RANGE(exp_ary));
+ UTEST_ASSERT(full_list != exp_list);
+ full_list.erase_after(full_list.begin());
+ UTEST_ASSERT(full_list == exp_list);
+ }
+ return utest_end() ? 0 : 1;
+}
diff --git a/src/util/list-container.h b/src/util/list-container.h
new file mode 100644
index 000000000..ca614314c
--- /dev/null
+++ b/src/util/list-container.h
@@ -0,0 +1,352 @@
+/*
+ * Inkscape::Util::ListContainer - encapsulates lists as STL containers,
+ * providing fast appending
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2005 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_UTIL_LIST_CONTAINER_H
+#define SEEN_INKSCAPE_UTIL_LIST_CONTAINER_H
+
+#include <limits>
+#include "util/list.h"
+
+namespace Inkscape {
+
+namespace Util {
+
+template <typename T>
+class ListContainer {
+public:
+ /* default constructible */
+ ListContainer() {}
+
+ /* assignable */
+ ListContainer(ListContainer const &other) {
+ *this = other;
+ }
+ ListContainer &operator=(ListContainer const &other) {
+ clear();
+ for ( const_iterator iter = other.begin() ; iter ; ++iter ) {
+ push_back(*iter);
+ }
+ return *this;
+ }
+ void swap(ListContainer<T> &other) {
+ std::swap(other._head, _head);
+ std::swap(other._tail, _tail);
+ }
+
+ /* equality comparable */
+ bool operator==(ListContainer const &other) const {
+ const_iterator iter = _head;
+ const_iterator other_iter = other._head;
+ while ( iter && other_iter ) {
+ if (!( *iter == *other_iter )) {
+ return false;
+ }
+ ++iter;
+ ++other_iter;
+ }
+ return !iter && !other_iter;
+ }
+ bool operator!=(ListContainer const &other) const {
+ return !operator==(other);
+ }
+
+ /* lessthan comparable */
+ bool operator<(ListContainer const &other) const {
+ const_iterator iter = _head;
+ const_iterator other_iter = other._head;
+ while ( iter && other_iter ) {
+ if ( *iter < *other_iter ) {
+ return true;
+ } else if ( *other_iter < *iter ) {
+ return false;
+ }
+ ++iter;
+ ++other_iter;
+ }
+ return bool(other_iter);
+ }
+ bool operator>=(ListContainer const &other) const {
+ return !operator<(other);
+ }
+
+ /* container */
+ typedef typename List<T>::value_type value_type;
+ typedef List<T> iterator;
+ typedef List<T const> const_iterator;
+ typedef typename List<T>::reference reference;
+ typedef typename List<T>::const_reference const_reference;
+ typedef typename List<T>::pointer pointer;
+ typedef typename List<T>::difference_type difference_type;
+ typedef std::size_t size_type;
+
+ iterator begin() { return _head; }
+ const_iterator begin() const { return _head; }
+ iterator end() { return iterator(); }
+ const_iterator end() const { return const_iterator(); }
+ size_type size() const {
+ size_type size=0;
+ for ( iterator iter = _head ; iter ; ++iter ) {
+ size++;
+ }
+ return size;
+ }
+ size_type max_size() const {
+ return std::numeric_limits<std::size_t>::max();
+ }
+ bool empty() const { return !_head; }
+
+ /* sequence */
+ ListContainer(size_type count, const_reference value) {
+ for ( ; count ; --count ) {
+ push_back(value);
+ }
+ }
+ ListContainer(size_type count) {
+ value_type default_value;
+ for ( ; count ; --count ) {
+ push_back(default_value);
+ }
+ }
+ template <typename ForwardIterator>
+ ListContainer(ForwardIterator i, ForwardIterator j) {
+ for ( ; i != j ; ++i ) {
+ push_back(*i);
+ }
+ }
+
+ reference front() { return *_head; }
+ const_reference front() const { return *_head; }
+
+ iterator insert(const_iterator position, const_reference value) {
+ if (position) {
+ if ( position != _head ) {
+ MutableList<T> added(value);
+ MutableList<T> before=_before(position);
+ set_rest(added, rest(before));
+ set_rest(before, added);
+ return added;
+ } else {
+ push_front(value);
+ return _head;
+ }
+ } else {
+ push_back(value);
+ return _tail;
+ }
+ }
+ void insert(const_iterator position, size_type count, const_reference value)
+ {
+ _insert_from_temp(position, ListContainer(count, value));
+ }
+ template <typename ForwardIterator>
+ void insert(const_iterator position, ForwardIterator i, ForwardIterator j) {
+ _insert_from_temp(position, ListContainer(i, j));
+ }
+ void erase(const_iterator position) {
+ erase(position, rest(position));
+ }
+ void erase(const_iterator i, const_iterator j) {
+ if ( i == _head ) {
+ _head = static_cast<MutableList<T> &>(j);
+ if ( !j || !rest(j) ) {
+ _tail = _head;
+ }
+ } else {
+ MutableList<T> before=_before(i);
+ if (j) {
+ set_rest(before, static_cast<MutableList<T> &>(j));
+ } else {
+ set_rest(before, MutableList<T>());
+ _tail = before;
+ }
+ }
+ }
+ void clear() {
+ _head = _tail = MutableList<T>();
+ }
+ void resize(size_type size, const_reference fill) {
+ MutableList<T> before;
+ MutableList<T> iter;
+ for ( iter = _head ; iter && size ; ++iter ) {
+ before = iter;
+ size--;
+ }
+ if (size) {
+ ListContainer temp(size, fill);
+ if (empty()) {
+ _head = temp._head;
+ _tail = temp._tail;
+ } else {
+ set_rest(_tail, temp._head);
+ _tail = temp._tail;
+ }
+ } else if (iter) {
+ if (before) {
+ set_rest(before, MutableList<T>());
+ _tail = before;
+ } else {
+ _head = _tail = MutableList<T>();
+ }
+ }
+ }
+ void resize(size_type size) {
+ resize(size, value_type());
+ }
+
+ /* front insertion sequence */
+ void push_front(const_reference value) {
+ if (_head) {
+ _head = cons(value, _head);
+ } else {
+ _head = _tail = MutableList<T>(value);
+ }
+ }
+ void pop_front() {
+ if (_head) {
+ _head = rest(_head);
+ if (!_head) {
+ _tail = _head;
+ }
+ }
+ }
+
+ /* back insertion sequence */
+ reference back() { return *_tail; }
+ const_reference back() const { return *_tail; }
+ void push_back(const_reference value) {
+ if (_tail) {
+ MutableList<T> added(value);
+ set_rest(_tail, added);
+ _tail = added;
+ } else {
+ _head = _tail = MutableList<T>(value);
+ }
+ }
+ // we're not required to provide pop_back if we can't
+ // implement it efficiently
+
+ /* additional */
+ MutableList<T> detatchList() {
+ MutableList<T> list=_head;
+ _head = _tail = MutableList<T>();
+ return list;
+ }
+ iterator insert_after(const_iterator pos, const_reference value) {
+ MutableList<T> added(value);
+ if (pos) {
+ MutableList<T> before=static_cast<MutableList<T> &>(pos);
+ set_rest(added, rest(before));
+ set_rest(before, added);
+ if ( _tail == before ) {
+ _tail = added;
+ }
+ } else {
+ push_front(value);
+ }
+ }
+ void insert_after(const_iterator position, size_type count,
+ const_reference value)
+ {
+ _insert_after_from_temp(position, ListContainer(count, value));
+ }
+ template <typename ForwardIterator>
+ void insert_after(const_iterator position,
+ ForwardIterator i, ForwardIterator j)
+ {
+ _insert_after_from_temp(position, ListContainer(i, j));
+ }
+ void erase_after(const_iterator position) {
+ if (!position) {
+ pop_front();
+ } else {
+ MutableList<T> before=static_cast<MutableList<T> &>(position);
+ MutableList<T> removed=rest(before);
+ set_rest(before, rest(removed));
+ if ( removed == _tail ) {
+ _tail = before;
+ }
+ }
+ }
+
+private:
+ MutableList<T> _head;
+ MutableList<T> _tail;
+
+ MutableList<T> _before(const_iterator position) {
+ for ( MutableList<T> iter = _head ; iter ; ++iter ) {
+ if ( rest(iter) == position ) {
+ return iter;
+ }
+ }
+ return MutableList<T>();
+ }
+ void _insert_from_temp(const_iterator pos, ListContainer const &temp) {
+ if (temp.empty()) {
+ return;
+ }
+ if (empty()) { /* if empty, just take the whole thing */
+ _head = temp._head;
+ _tail = temp._tail;
+ } else if (pos) {
+ if ( pos == _head ) { /* prepend */
+ set_rest(temp._tail, _head);
+ _head = temp._head;
+ } else { /* insert somewhere in the middle */
+ MutableList<T> before=_before(pos);
+ set_rest(temp._tail, static_cast<MutableList<T> &>(pos));
+ set_rest(before, temp._head);
+ }
+ } else { /* append */
+ set_rest(_tail, temp._head);
+ _tail = temp._tail;
+ }
+ }
+ void _insert_after_from_temp(const_iterator pos,
+ ListContainer const &temp)
+ {
+ if (temp.empty()) {
+ return;
+ }
+ if (empty()) { /* if empty, just take the whole thing */
+ _head = temp._head;
+ _tail = temp._tail;
+ } else if (pos) {
+ if ( pos == _tail ) { /* append */
+ set_rest(_tail, temp._head);
+ _tail = temp._tail;
+ } else { /* insert somewhere in the middle */
+ MutableList<T> before=static_cast<MutableList<T> &>(pos);
+ set_rest(temp._tail, rest(before));
+ set_rest(before, temp._head);
+ }
+ } else { /* prepend */
+ set_rest(temp._tail, _head);
+ _head = temp._head;
+ }
+ }
+};
+
+}
+
+}
+
+#endif
+/*
+ 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 :
diff --git a/src/util/list.h b/src/util/list.h
new file mode 100644
index 000000000..86a73711a
--- /dev/null
+++ b/src/util/list.h
@@ -0,0 +1,407 @@
+/** \file
+ * Inkscape::Util::List - managed linked list
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_UTIL_LIST_H
+#define SEEN_INKSCAPE_UTIL_LIST_H
+
+#include <cstddef>
+#include <iterator>
+#include "gc-managed.h"
+#include "traits/reference.h"
+
+namespace Inkscape {
+
+namespace Util {
+
+/// Generic ListCell for Inkscape::Util::List.
+template <typename T>
+struct ListCell : public GC::Managed<> {
+ ListCell() {}
+ ListCell(typename Traits::Reference<T>::RValue v, ListCell *n)
+ : value(v), next(n) {}
+
+ T value;
+ ListCell *next;
+};
+
+template <typename T> class List;
+template <typename T> class MutableList;
+
+template <typename T>
+bool is_empty(List<T> const &list);
+
+template <typename T>
+typename List<T>::reference first(List<T> const &list);
+
+template <typename T>
+List<T> const &rest(List<T> const &list);
+
+template <typename T>
+MutableList<T> &rest(MutableList<T> const &list);
+
+template <typename T>
+MutableList<T> const &set_rest(MutableList<T> const &list,
+ MutableList<T> const &rest);
+
+/// Helper template.
+template <typename T>
+class List<T const> {
+public:
+ typedef std::forward_iterator_tag iterator_category;
+ typedef T const value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef typename Traits::Reference<value_type>::LValue reference;
+ typedef typename Traits::Reference<value_type>::RValue const_reference;
+ typedef typename Traits::Reference<value_type>::Pointer pointer;
+
+ List() : _cell(NULL) {}
+ explicit List(const_reference value, List const &next=List())
+ : _cell(new ListCell<T>(value, next._cell)) {}
+
+ operator bool() const { return this->_cell; }
+
+ reference operator*() const { return this->_cell->value; }
+ pointer operator->() const { return &this->_cell->value; }
+
+ bool operator==(List const &other) const {
+ return this->_cell == other._cell;
+ }
+ bool operator!=(List const &other) const {
+ return this->_cell != other._cell;
+ }
+
+ List &operator++() {
+ this->_cell = this->_cell->next;
+ return *this;
+ }
+ List operator++(int) {
+ List old(*this);
+ this->_cell = this->_cell->next;
+ return old;
+ }
+
+ friend reference first<>(List const &);
+ friend List const &rest<>(List const &);
+ friend bool is_empty<>(List const &);
+
+protected:
+ ListCell<T> *_cell;
+};
+
+/**
+ * Generic linked list.
+ *
+ * These lists are designed to store simple values like pointers,
+ * references, and scalar values. While they can be used to directly
+ * store more complex objects, destructors for those objects will not
+ * be called unless those objects derive from Inkscape::GC::Finalized.
+ *
+ * In general it's better to use lists to store pointers or references
+ * to objects requiring finalization and manage object lifetimes separately.
+ *
+ * @see Inkscape::GC::Finalized
+ *
+ * cons() is synonymous with List<T>(first, rest), except that the
+ * compiler will usually be able to infer T from the type of \a rest.
+ *
+ * If you need to create an empty list (which can, for example, be used
+ * as an 'end' value with STL algorithms), call the List<> constructor
+ * with no arguments, like so:
+ *
+ * <code> List<int>() </code>
+ */
+template <typename T>
+class List : public List<T const> {
+public:
+ typedef T value_type;
+ typedef typename Traits::Reference<value_type>::LValue reference;
+ typedef typename Traits::Reference<value_type>::RValue const_reference;
+ typedef typename Traits::Reference<value_type>::Pointer pointer;
+
+ List() : List<T const>() {}
+ explicit List(const_reference value, List const &next=List())
+ : List<T const>(value, next) {}
+
+ reference operator*() const { return this->_cell->value; }
+ pointer operator->() const { return &this->_cell->value; }
+
+ List &operator++() {
+ this->_cell = this->_cell->next;
+ return *this;
+ }
+ List operator++(int) {
+ List old(*this);
+ this->_cell = this->_cell->next;
+ return old;
+ }
+
+ friend reference first<>(List const &);
+ friend List const &rest<>(List const &);
+ friend bool is_empty<>(List const &);
+};
+
+/// Helper template.
+template <typename T>
+class List<T &> {
+public:
+ typedef std::forward_iterator_tag iterator_category;
+ typedef T &value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef typename Traits::Reference<value_type>::LValue reference;
+ typedef typename Traits::Reference<value_type>::RValue const_reference;
+ typedef typename Traits::Reference<value_type>::Pointer pointer;
+
+ List() : _cell(NULL) {}
+ List(const_reference value, List const &next=List())
+ : _cell(new ListCell<T &>(value, next._cell)) {}
+
+ operator bool() const { return this->_cell; }
+
+ reference operator*() const { return this->_cell->value; }
+ pointer operator->() const { return &this->_cell->value; }
+
+ bool operator==(List const &other) const {
+ return this->_cell == other._cell;
+ }
+ bool operator!=(List const &other) const {
+ return this->_cell != other._cell;
+ }
+
+ List &operator++() {
+ this->_cell = this->_cell->next;
+ return *this;
+ }
+ List operator++(int) {
+ List old(*this);
+ this->_cell = this->_cell->next;
+ return old;
+ }
+
+ friend reference first<>(List const &);
+ friend List const &rest<>(List const &);
+ friend bool is_empty<>(List const &);
+
+protected:
+ ListCell<T &> *_cell;
+};
+
+/**
+ * Generic MutableList.
+ *
+ * Like a linked list, but one whose tail can be exchanged for
+ * another later by using set_rest() or assignment through rest()
+ * as an lvalue. It's otherwise identical to the "non-mutable" form.
+ *
+ * As with List, you can create an empty list like so:
+ *
+ * <code> MutableList<int>() </code>
+ */
+template <typename T>
+class MutableList : public List<T> {
+public:
+ MutableList() {}
+ explicit MutableList(typename List<T>::const_reference value,
+ MutableList const &next=MutableList())
+ : List<T>(value, next) {}
+
+ MutableList &operator++() {
+ this->_cell = this->_cell->next;
+ return *this;
+ }
+ MutableList operator++(int) {
+ MutableList old(*this);
+ this->_cell = this->_cell->next;
+ return old;
+ }
+
+ friend MutableList &rest<>(MutableList const &);
+ friend MutableList const &set_rest<>(MutableList const &,
+ MutableList const &);
+};
+
+/** @brief Creates a (non-empty) linked list.
+ *
+ * Creates a new linked list with a copy of the given value (\a first)
+ * in its first element; the remainder of the list will be the list
+ * provided as \a rest.
+ *
+ * The remainder of the list -- the "tail" -- is incorporated by
+ * reference rather than being copied.
+ *
+ * The returned value can also be treated as an STL forward iterator.
+ *
+ * @param first the value for the first element of the list
+ * @param rest the rest of the list; may be an empty list
+ *
+ * @returns a new list
+ *
+ * @see List<>
+ * @see is_empty<>
+ *
+ */
+template <typename T>
+inline List<T> cons(typename Traits::Reference<T>::RValue first,
+ List<T> const &rest)
+{
+ return List<T>(first, rest);
+}
+
+/** @brief Creates a (non-empty) linked list whose tail can be exchanged
+ * for another.
+ *
+ * Creates a new linked list, but one whose tail can be exchanged for
+ * another later by using set_rest() or assignment through rest()
+ * as an lvalue. It's otherwise identical to the "non-mutable" form.
+ *
+ * This form of cons() is synonymous with MutableList<T>(first, rest),
+ * except that the compiler can usually infer T from the type of \a rest.
+ *
+ * As with List<>, you can create an empty list like so:
+ *
+ * MutableList<int>()
+ *
+ * @see MutableList<>
+ * @see is_empty<>
+ *
+ * @param first the value for the first element of the list
+ * @param rest the rest of the list; may be an empty list
+ *
+ * @returns a new list
+ */
+template <typename T>
+inline MutableList<T> cons(typename Traits::Reference<T>::RValue first,
+ MutableList<T> const &rest)
+{
+ return MutableList<T>(first, rest);
+}
+
+/** @brief Returns true if the given list is empty.
+ *
+ * Returns true if the given list is empty. This is equivalent
+ * to !list.
+ *
+ * @param list the list
+ *
+ * @returns true if the list is empty, false otherwise.
+ */
+template <typename T>
+inline bool is_empty(List<T> const &list) { return !list._cell; }
+
+/** @brief Returns the first value in a linked list.
+ *
+ * Returns a reference to the first value in the list. This
+ * corresponds to the value of the first argument passed to cons().
+ *
+ * If the list holds mutable values (or references to them), first()
+ * can be used as an lvalue.
+ *
+ * For example:
+ *
+ * first(list) = value;
+ *
+ * The results of calling this on an empty list are undefined.
+ *
+ * @see cons<>
+ * @see is_empty<>
+ *
+ * @param list the list; cannot be empty
+ *
+ * @returns a reference to the first value in the list
+ */
+template <typename T>
+inline typename List<T>::reference first(List<T> const &list) {
+ return list._cell->value;
+}
+
+/** @brief Returns the remainder of a linked list after the first element.
+ *
+ * Returns the remainder of the list after the first element (its "tail").
+ *
+ * This will be the same as the second argument passed to cons().
+ *
+ * The results of calling this on an empty list are undefined.
+ *
+ * @see cons<>
+ * @see is_empty<>
+ *
+ * @param list the list; cannot be empty
+ *
+ * @returns the remainder of the list
+ */
+template <typename T>
+inline List<T> const &rest(List<T> const &list) {
+ return reinterpret_cast<List<T> const &>(list._cell->next);
+}
+
+/** @brief Returns a reference to the remainder of a linked list after
+ * the first element.
+ *
+ * Returns a reference to the remainder of the list after the first
+ * element (its "tail"). For MutableList<>, rest() can be used as
+ * an lvalue, to set a new tail.
+ *
+ * For example:
+ *
+ * rest(list) = other;
+ *
+ * Results of calling this on an empty list are undefined.
+ *
+ * @see cons<>
+ * @see is_empty<>
+ *
+ * @param list the list; cannot be empty
+ *
+ * @returns a reference to the remainder of the list
+ */
+template <typename T>
+inline MutableList<T> &rest(MutableList<T> const &list) {
+ return reinterpret_cast<MutableList<T> &>(list._cell->next);
+}
+
+/** @brief Sets a new tail for an existing linked list.
+ *
+ * Sets the tail of the given MutableList<>, corresponding to the
+ * second argument of cons().
+ *
+ * Results of calling this on an empty list are undefined.
+ *
+ * @see rest<>
+ * @see cons<>
+ * @see is_empty<>
+ *
+ * @param list the list; cannot be empty
+ * @param rest the new tail; corresponds to the second argument of cons()
+ *
+ * @returns the new tail
+ */
+template <typename T>
+inline MutableList<T> const &set_rest(MutableList<T> const &list,
+ MutableList<T> const &rest)
+{
+ list._cell->next = rest._cell;
+ return reinterpret_cast<MutableList<T> &>(list._cell->next);
+}
+
+}
+
+}
+
+#endif
+/*
+ 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 :
diff --git a/src/util/makefile.in b/src/util/makefile.in
new file mode 100644
index 000000000..fcaa079c0
--- /dev/null
+++ b/src/util/makefile.in
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+@SET_MAKE@
+
+# Explicit so that it's the default rule.
+all:
+ cd .. && $(MAKE) util/all
+
+clean %.a %.o:
+ cd .. && $(MAKE) util/$@
+
+.PHONY: all clean
+
+OBJEXT = @OBJEXT@
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)
diff --git a/src/util/map-list.h b/src/util/map-list.h
new file mode 100644
index 000000000..8eba81e3c
--- /dev/null
+++ b/src/util/map-list.h
@@ -0,0 +1,68 @@
+/*
+ * Inkscape::Util::map_list - apply a function over a list
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_UTIL_MAP_LIST_H
+#define SEEN_INKSCAPE_UTIL_MAP_LIST_H
+
+#include <algorithm>
+#include "util/list.h"
+
+namespace Inkscape {
+
+namespace Util {
+
+template <typename T, typename InputIterator, typename UnaryFunction>
+inline MutableList<T>
+map_list(UnaryFunction f, InputIterator start, InputIterator end)
+{
+ if ( start != end ) {
+ MutableList<T> head(f(*start));
+ MutableList<T> tail(head);
+ while ( ++start != end ) {
+ MutableList<T> cell(f(*start));
+ set_rest(tail, cell);
+ tail = cell;
+ }
+ return head;
+ } else {
+ return MutableList<T>();
+ }
+}
+
+template <typename T1, typename T2, typename UnaryFunction>
+inline MutableList<T1> map_list(UnaryFunction f, List<T2> list) {
+ return map_list(f, list, List<T2>());
+}
+
+template <typename T, typename UnaryFunction>
+inline List<T>
+map_list_in_place(UnaryFunction f, List<T> start,
+ List<T> end=List<T>())
+{
+ std::transform(start, end, start, f);
+ return start;
+}
+
+}
+
+}
+
+#endif
+/*
+ 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 :
diff --git a/src/util/reverse-list.h b/src/util/reverse-list.h
new file mode 100644
index 000000000..586f706c7
--- /dev/null
+++ b/src/util/reverse-list.h
@@ -0,0 +1,68 @@
+/*
+ * Inkscape::Util::reverse_list - generate a reversed list from iterator range
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_UTIL_REVERSE_LIST_H
+#define SEEN_INKSCAPE_UTIL_REVERSE_LIST_H
+
+#include "util/list.h"
+#include "traits/list-copy.h"
+
+namespace Inkscape {
+
+namespace Util {
+
+template <typename InputIterator>
+inline typename Traits::ListCopy<InputIterator>::ResultList
+reverse_list(InputIterator start, InputIterator end) {
+ typename Traits::ListCopy<InputIterator>::ResultList head;
+ while ( start != end ) {
+ head = cons(*start, head);
+ ++start;
+ }
+ return head;
+}
+
+template <typename T>
+inline typename Traits::ListCopy<List<T> >::ResultList
+reverse_list(List<T> const &list) {
+ return reverse_list(list, List<T>());
+}
+
+template <typename T>
+inline MutableList<T>
+reverse_list_in_place(MutableList<T> start,
+ MutableList<T> end=MutableList<T>())
+{
+ MutableList<T> reversed(end);
+ while ( start != end ) {
+ MutableList<T> temp(start);
+ ++start;
+ set_rest(temp, reversed);
+ reversed = temp;
+ }
+ return reversed;
+}
+
+}
+
+}
+
+#endif
+/*
+ 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 :
diff --git a/src/util/shared-c-string-ptr.cpp b/src/util/shared-c-string-ptr.cpp
new file mode 100644
index 000000000..cedf18df4
--- /dev/null
+++ b/src/util/shared-c-string-ptr.cpp
@@ -0,0 +1,50 @@
+/*
+ * Inkscape::Util::SharedCStringPtr - shared and immutable strings
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <cstring>
+#include <glib/gmessages.h>
+#include "gc-core.h"
+#include "util/shared-c-string-ptr.h"
+
+namespace Inkscape {
+
+namespace Util {
+
+SharedCStringPtr SharedCStringPtr::copy(char const *string) {
+ g_return_val_if_fail(string != NULL, SharedCStringPtr::coerce(NULL));
+
+ return SharedCStringPtr::copy(string, std::strlen(string));
+}
+
+SharedCStringPtr SharedCStringPtr::copy(char const *string, size_t len) {
+ g_return_val_if_fail(string != NULL, SharedCStringPtr::coerce(NULL));
+
+ char *dup=new (GC::ATOMIC) gchar[len+1];
+ std::memcpy(dup, string, len);
+ dup[len] = '\000';
+
+ return SharedCStringPtr::coerce(dup);
+}
+
+}
+
+}
+
+/*
+ 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 :
diff --git a/src/util/shared-c-string-ptr.h b/src/util/shared-c-string-ptr.h
new file mode 100644
index 000000000..b88f4cf99
--- /dev/null
+++ b/src/util/shared-c-string-ptr.h
@@ -0,0 +1,77 @@
+/*
+ * Inkscape::Util::SharedCStringPtr - shared and immutable strings
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_UTIL_SHARED_C_STRING_PTR_H
+#define SEEN_INKSCAPE_UTIL_SHARED_C_STRING_PTR_H
+
+#include <sys/types.h>
+#include <glib/gtypes.h>
+
+namespace Inkscape {
+
+namespace Util {
+
+class SharedCStringPtr {
+public:
+ SharedCStringPtr() : _str(NULL) {}
+
+ operator char const *() const { return cString(); }
+
+ char operator[](size_t i) const { return cString()[i]; }
+
+ char const *cString() const { return _str; }
+
+ static SharedCStringPtr coerce(char const *s) { return SharedCStringPtr(s); }
+ static SharedCStringPtr copy(char const *s);
+ static SharedCStringPtr copy(char const *s, size_t len);
+
+ operator bool() const { return _str; }
+
+ bool operator==(SharedCStringPtr const &other) { return _str == other._str; }
+ bool operator!=(SharedCStringPtr const &other) { return _str != other._str; }
+
+private:
+ SharedCStringPtr(char const *s) : _str(s) {}
+
+ char const *_str;
+};
+
+inline bool operator==(SharedCStringPtr const &ss, char const *s) {
+ return ss.cString() == s;
+}
+
+inline bool operator==(char const *s, SharedCStringPtr const &ss) {
+ return operator==(ss, s);
+}
+
+inline bool operator!=(SharedCStringPtr const &ss, char const *s) {
+ return !operator==(ss, s);
+}
+
+inline bool operator!=(char const *s, SharedCStringPtr const &ss) {
+ return !operator==(s, ss);
+}
+
+}
+
+}
+
+#endif
+/*
+ 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 :
diff --git a/src/util/tuple.h b/src/util/tuple.h
new file mode 100644
index 000000000..bf9338366
--- /dev/null
+++ b/src/util/tuple.h
@@ -0,0 +1,182 @@
+/*
+ * Inkscape::Util::Tuple - generic tuple type
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_UTIL_TUPLE_H
+#define SEEN_INKSCAPE_UTIL_TUPLE_H
+
+#include "traits/reference.h"
+
+namespace Inkscape {
+
+namespace Util {
+
+template <typename A=void, typename B=void, typename C=void,
+ typename D=void, typename E=void, typename F=void>
+struct Tuple {
+ Tuple() {}
+ Tuple(typename Traits::Reference<A>::RValue a_,
+ typename Traits::Reference<B>::RValue b_,
+ typename Traits::Reference<C>::RValue c_,
+ typename Traits::Reference<D>::RValue d_,
+ typename Traits::Reference<E>::RValue e_,
+ typename Traits::Reference<F>::RValue f_)
+ : a(a_), b(b_), c(c_), d(d_), e(e_), f(f_) {}
+
+ A a;
+ B b;
+ C c;
+ D d;
+ E e;
+ F f;
+};
+
+template <typename A, typename B, typename C,
+ typename D, typename E>
+struct Tuple<A, B, C, D, E, void> {
+ Tuple() {}
+ Tuple(typename Traits::Reference<A>::RValue a_,
+ typename Traits::Reference<B>::RValue b_,
+ typename Traits::Reference<C>::RValue c_,
+ typename Traits::Reference<D>::RValue d_,
+ typename Traits::Reference<E>::RValue e_)
+ : a(a_), b(b_), c(c_), d(d_), e(e_) {}
+
+ A a;
+ B b;
+ C c;
+ D d;
+ E e;
+};
+
+template <typename A, typename B, typename C, typename D>
+struct Tuple<A, B, C, D, void, void> {
+ Tuple() {}
+ Tuple(typename Traits::Reference<A>::RValue a_,
+ typename Traits::Reference<B>::RValue b_,
+ typename Traits::Reference<C>::RValue c_,
+ typename Traits::Reference<D>::RValue d_)
+ : a(a_), b(b_), c(c_), d(d_) {}
+
+ A a;
+ B b;
+ C c;
+ D d;
+};
+
+template <typename A, typename B, typename C>
+struct Tuple<A, B, C, void, void, void> {
+ Tuple() {}
+ Tuple(typename Traits::Reference<A>::RValue a_,
+ typename Traits::Reference<B>::RValue b_,
+ typename Traits::Reference<C>::RValue c_)
+ : a(a_), b(b_), c(c_) {}
+
+ A a;
+ B b;
+ C c;
+};
+
+template <typename A, typename B>
+struct Tuple<A, B, void, void, void, void> {
+ Tuple() {}
+ Tuple(typename Traits::Reference<A>::RValue a_,
+ typename Traits::Reference<B>::RValue b_)
+ : a(a_), b(b_) {}
+
+ A a;
+ B b;
+};
+
+template <typename A>
+struct Tuple<A, void, void, void, void, void> {
+ Tuple() {}
+ Tuple(typename Traits::Reference<A>::RValue a_)
+ : a(a_) {}
+
+ A a;
+};
+
+template <> struct Tuple<void, void, void, void, void, void> {};
+
+template <typename A, typename B, typename C,
+ typename D, typename E, typename F>
+inline Tuple<A, B, C, D, E, F>
+tuple(typename Traits::Reference<A>::RValue a,
+ typename Traits::Reference<B>::RValue b,
+ typename Traits::Reference<C>::RValue c,
+ typename Traits::Reference<D>::RValue d,
+ typename Traits::Reference<E>::RValue e,
+ typename Traits::Reference<F>::RValue f)
+{
+ return Tuple<A, B, C, D, E, F>(a, b, c, d, e, f);
+}
+
+template <typename A, typename B, typename C, typename D, typename E>
+inline Tuple<A, B, C, D, E>
+tuple(typename Traits::Reference<A>::RValue a,
+ typename Traits::Reference<B>::RValue b,
+ typename Traits::Reference<C>::RValue c,
+ typename Traits::Reference<D>::RValue d,
+ typename Traits::Reference<E>::RValue e)
+{
+ return Tuple<A, B, C, D, E>(a, b, c, d, e);
+}
+
+template <typename A, typename B, typename C, typename D>
+inline Tuple<A, B, C, D>
+tuple(typename Traits::Reference<A>::RValue a,
+ typename Traits::Reference<B>::RValue b,
+ typename Traits::Reference<C>::RValue c,
+ typename Traits::Reference<D>::RValue d)
+{
+ return Tuple<A, B, C, D>(a, b, c, d);
+}
+
+template <typename A, typename B, typename C>
+inline Tuple<A, B, C>
+tuple(typename Traits::Reference<A>::RValue a,
+ typename Traits::Reference<B>::RValue b,
+ typename Traits::Reference<C>::RValue c)
+{
+ return Tuple<A, B, C>(a, b, c);
+}
+
+template <typename A, typename B>
+inline Tuple<A, B>
+tuple(typename Traits::Reference<A>::RValue a,
+ typename Traits::Reference<B>::RValue b)
+{
+ return Tuple<A, B>(a, b);
+}
+
+template <typename A>
+inline Tuple<A>
+tuple(typename Traits::Reference<A>::RValue a) {
+ return Tuple<A>(a);
+}
+
+inline Tuple<> tuple() { return Tuple<>(); }
+
+}
+
+}
+
+#endif
+/*
+ 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 :
diff --git a/src/util/ucompose.hpp b/src/util/ucompose.hpp
new file mode 100644
index 000000000..d29cf5424
--- /dev/null
+++ b/src/util/ucompose.hpp
@@ -0,0 +1,438 @@
+/* Defines String::ucompose(fmt, arg...) for easy, i18n-friendly
+ * composition of strings with Gtkmm >= 1.3.* (see www.gtkmm.org).
+ * Uses Glib::ustring instead of std::string which doesn't work with
+ * Gtkmm due to character encoding troubles with stringstreams.
+ *
+ * Version 1.0.4.
+ *
+ * Copyright (c) 2002, 03, 04 Ole Laursen <olau@hardworking.dk>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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.
+ */
+
+//
+// Basic usage is like
+//
+// String::ucompose("This is a %1x%2 matrix.", rows, cols);
+//
+// See http://www.cs.aau.dk/~olau/compose/ or the included
+// README.compose for more details.
+//
+
+#ifndef STRING_UCOMPOSE_HPP
+#define STRING_UCOMPOSE_HPP
+
+#include <glibmm/ustring.h>
+#include <glibmm/convert.h>
+
+#include <sstream>
+#include <string>
+#include <list>
+#include <map> // for multimap
+
+namespace UStringPrivate
+{
+ // the actual composition class - using String::ucompose is cleaner, so we
+ // hide it here
+ class Composition
+ {
+ public:
+ // initialize and prepare format string on the form "text %1 text %2 etc."
+ explicit Composition(std::string fmt);
+
+ // supply an replacement argument starting from %1
+ template <typename T>
+ Composition &arg(const T &obj);
+
+ // compose and return string
+ Glib::ustring str() const;
+
+ private:
+
+ //This is standard, not GCC-specific like wostringstream
+ std::basic_ostringstream<wchar_t> os;
+
+ int arg_no;
+
+ // we store the output as a list - when the output string is requested, the
+ // list is concatenated to a string; this way we can keep iterators into
+ // the list instead of into a string where they're possibly invalidated
+ // when inserting a specification string
+ typedef std::list<std::string> output_list;
+ output_list output;
+
+ // the initial parse of the format string fills in the specification map
+ // with positions for each of the various %?s
+ typedef std::multimap<int, output_list::iterator> specification_map;
+ specification_map specs;
+
+ template <typename T>
+ std::string stringify(T obj);
+ };
+
+ // helper for converting spec string numbers
+ inline int char_to_int(char c)
+ {
+ switch (c) {
+ case '0': return 0;
+ case '1': return 1;
+ case '2': return 2;
+ case '3': return 3;
+ case '4': return 4;
+ case '5': return 5;
+ case '6': return 6;
+ case '7': return 7;
+ case '8': return 8;
+ case '9': return 9;
+ default: return -1000;
+ }
+ }
+
+ inline bool is_number(int n)
+ {
+ switch (n) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ template <typename T>
+ inline std::string Composition::stringify(T obj)
+ {
+ os << obj;
+
+ std::wstring str = os.str();
+
+ return Glib::convert(std::string(reinterpret_cast<const char *>(str.data()),
+ str.size() * sizeof(wchar_t)),
+ "UTF-8", "WCHAR_T");
+ }
+
+ // specialisations for the common string types
+ template <>
+ inline std::string
+ Composition::stringify<std::string>(std::string obj)
+ {
+ return obj;
+ }
+
+ template <>
+ inline std::string
+ Composition::stringify<Glib::ustring>(Glib::ustring obj)
+ {
+ return obj;
+ }
+
+ template <>
+ inline std::string
+ Composition::stringify<const char *>(const char *obj)
+ {
+ return obj;
+ }
+
+ // implementation of class Composition
+ template <typename T>
+ inline Composition &Composition::arg(const T &obj)
+ {
+ Glib::ustring rep = stringify(obj);
+
+ if (!rep.empty()) { // manipulators don't produce output
+ for (specification_map::const_iterator i = specs.lower_bound(arg_no),
+ end = specs.upper_bound(arg_no); i != end; ++i) {
+ output_list::iterator pos = i->second;
+ ++pos;
+
+ output.insert(pos, rep);
+ }
+
+ os.str(std::wstring());
+ //os.clear();
+ ++arg_no;
+ }
+
+ return *this;
+ }
+
+ inline Composition::Composition(std::string fmt)
+ : arg_no(1)
+ {
+#if __GNUC__ >= 3
+ os.imbue(std::locale("")); // use the user's locale for the stream
+#endif
+ std::string::size_type b = 0, i = 0;
+
+ // fill in output with the strings between the %1 %2 %3 etc. and
+ // fill in specs with the positions
+ while (i < fmt.length()) {
+ if (fmt[i] == '%' && i + 1 < fmt.length()) {
+ if (fmt[i + 1] == '%') { // catch %%
+ fmt.replace(i, 2, "%");
+ ++i;
+ }
+ else if (is_number(fmt[i + 1])) { // aha! a spec!
+ // save string
+ output.push_back(fmt.substr(b, i - b));
+
+ int n = 1; // number of digits
+ int spec_no = 0;
+
+ do {
+ spec_no += char_to_int(fmt[i + n]);
+ spec_no *= 10;
+ ++n;
+ } while (i + n < fmt.length() && is_number(fmt[i + n]));
+
+ spec_no /= 10;
+ output_list::iterator pos = output.end();
+ --pos; // safe since we have just inserted a string
+
+ specs.insert(specification_map::value_type(spec_no, pos));
+
+ // jump over spec string
+ i += n;
+ b = i;
+ }
+ else
+ ++i;
+ }
+ else
+ ++i;
+ }
+
+ if (i - b > 0) // add the rest of the string
+ output.push_back(fmt.substr(b, i - b));
+ }
+
+ inline Glib::ustring Composition::str() const
+ {
+ // assemble string
+ std::string str;
+
+ for (output_list::const_iterator i = output.begin(), end = output.end();
+ i != end; ++i)
+ str += *i;
+
+ return str;
+ }
+}
+
+
+namespace String
+{
+ // a series of functions which accept a format string on the form "text %1
+ // more %2 less %3" and a number of templated parameters and spits out the
+ // composited string
+ template <typename T1>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt, const T1 &o1)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1);
+ return c.str();
+ }
+
+ template <typename T1, typename T2>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13, const T14 &o14)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
+ return c.str();
+ }
+
+ template <typename T1, typename T2, typename T3, typename T4, typename T5,
+ typename T6, typename T7, typename T8, typename T9, typename T10,
+ typename T11, typename T12, typename T13, typename T14,
+ typename T15>
+ inline Glib::ustring ucompose(const Glib::ustring &fmt,
+ const T1 &o1, const T2 &o2, const T3 &o3,
+ const T4 &o4, const T5 &o5, const T6 &o6,
+ const T7 &o7, const T8 &o8, const T9 &o9,
+ const T10 &o10, const T11 &o11, const T12 &o12,
+ const T13 &o13, const T14 &o14, const T15 &o15)
+ {
+ UStringPrivate::Composition c(fmt);
+ c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
+ .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
+ return c.str();
+ }
+}
+
+
+#endif // STRING_UCOMPOSE_HPP
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 :
diff --git a/src/util/units.h b/src/util/units.h
new file mode 100644
index 000000000..d45109a8f
--- /dev/null
+++ b/src/util/units.h
@@ -0,0 +1,88 @@
+/*
+This is a rough draft of a global 'units' thingee, to allow dialogs and
+the ruler to share info about unit systems... Dunno if this is the
+right kind of object though, so we may have to redo this or shift things
+around later when it becomes clearer what we need.
+
+This object is used for defining different unit systems.
+
+This is intended to eventually replace inkscape/helper/units.*.
+
+Need to review the Units support that's in Gtkmm already...
+
+*/
+
+#ifndef INKSCAPE_UTIL_UNITS_H
+#define INKSCAPE_UTIL_UNITS_H
+
+#include <map>
+#include <glibmm/ustring.h>
+
+namespace Inkscape {
+namespace Util {
+
+enum UnitType {
+ UNIT_TYPE_DIMENSIONLESS, /* Percentage */
+ UNIT_TYPE_LINEAR,
+ UNIT_TYPE_LINEAR_SCALED,
+ UNIT_TYPE_RADIAL,
+ UNIT_TYPE_TIME,
+ UNIT_TYPE_FONT_HEIGHT,
+ UNIT_TYPE_QTY,
+ UNIT_TYPE_NONE = -1
+};
+
+class Unit {
+ public:
+ Glib::ustring name;
+ Glib::ustring name_plural;
+ Glib::ustring abbr;
+ Glib::ustring description;
+
+ UnitType type;
+
+ double factor;
+
+ bool isAbsolute() const { return type != UNIT_TYPE_DIMENSIONLESS; }
+ int defaultDigits() const;
+};
+
+class UnitTable {
+ public:
+ UnitTable();
+ virtual ~UnitTable();
+
+ typedef std::map<Glib::ustring, Unit*> UnitMap;
+
+ void addUnit(Unit const& u, bool primary);
+ Unit getUnit(Glib::ustring const& name) const;
+ bool deleteUnit(Unit const& u);
+ bool hasUnit(Glib::ustring const &name) const;
+
+ UnitTable::UnitMap units(UnitType type) const;
+
+ Glib::ustring primary(UnitType type) const;
+
+ double getScale() const;
+ void setScale();
+
+ bool load(Glib::ustring const &filename);
+ bool loadText(Glib::ustring const &filename);
+ bool save(Glib::ustring const &filename);
+
+ protected:
+ UnitTable::UnitMap _unit_map;
+ Glib::ustring _primary_unit[UNIT_TYPE_QTY];
+
+ double _linear_scale;
+
+ private:
+ UnitTable(UnitTable const& t);
+ UnitTable operator=(UnitTable const& t);
+
+};
+
+} // namespace Util
+} // namespace Inkscape
+
+#endif // define INKSCAPE_UTIL_UNITS_H