From 179fa413b047bede6e32109e2ce82437c5fb8d34 Mon Sep 17 00:00:00 2001 From: MenTaLguY Date: Mon, 16 Jan 2006 02:36:01 +0000 Subject: moving trunk for module inkscape (bzr r1) --- src/util/.cvsignore | 5 + src/util/Makefile_insert | 25 ++ src/util/compose.hpp | 393 ++++++++++++++++++++++++++++++++ src/util/filter-list.h | 65 ++++++ src/util/forward-pointer-iterator.h | 120 ++++++++++ src/util/glib-list-iterators.h | 238 ++++++++++++++++++++ src/util/list-container-test.cpp | 204 +++++++++++++++++ src/util/list-container.h | 352 +++++++++++++++++++++++++++++ src/util/list.h | 407 +++++++++++++++++++++++++++++++++ src/util/makefile.in | 17 ++ src/util/map-list.h | 68 ++++++ src/util/reverse-list.h | 68 ++++++ src/util/shared-c-string-ptr.cpp | 50 ++++ src/util/shared-c-string-ptr.h | 77 +++++++ src/util/tuple.h | 182 +++++++++++++++ src/util/ucompose.hpp | 438 ++++++++++++++++++++++++++++++++++++ src/util/units.cpp | 344 ++++++++++++++++++++++++++++ src/util/units.h | 88 ++++++++ 18 files changed, 3141 insertions(+) create mode 100644 src/util/.cvsignore create mode 100644 src/util/Makefile_insert create mode 100644 src/util/compose.hpp create mode 100644 src/util/filter-list.h create mode 100644 src/util/forward-pointer-iterator.h create mode 100644 src/util/glib-list-iterators.h create mode 100644 src/util/list-container-test.cpp create mode 100644 src/util/list-container.h create mode 100644 src/util/list.h create mode 100644 src/util/makefile.in create mode 100644 src/util/map-list.h create mode 100644 src/util/reverse-list.h create mode 100644 src/util/shared-c-string-ptr.cpp create mode 100644 src/util/shared-c-string-ptr.h create mode 100644 src/util/tuple.h create mode 100644 src/util/ucompose.hpp create mode 100644 src/util/units.cpp create mode 100644 src/util/units.h (limited to 'src/util') 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 . + * + * 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 +#include +#include +#include // 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 + 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 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 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 + 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 + inline std::string compose(const std::string &fmt, const T1 &o1) + { + StringPrivate::Composition c(fmt); + c.arg(o1); + return c.str(); + } + + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + * + * 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 +inline typename Traits::ListCopy::ResultList +filter_list(UnaryPredicate p, InputIterator start, InputIterator end) { + typedef typename Traits::ListCopy::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 +inline typename Traits::ListCopy >::ResultList +filter_list(UnaryPredicate p, List const &list) { + return filter_list(p, list, 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/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 + * + * 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 +#include "traits/reference.h" + +namespace Inkscape { + +namespace Util { + +template +class ForwardPointerIterator; + +template +class ForwardPointerIterator { +public: + typedef std::forward_iterator_tag iterator_category; + typedef typename Traits::Reference::LValue value_type; + typedef std::ptrdiff_t difference_type; + typedef typename Traits::Reference::LValue reference; + typedef typename Traits::Reference::RValue const_reference; + typedef typename Traits::Reference::Pointer pointer; + + typedef ForwardPointerIterator 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 +class ForwardPointerIterator +: public ForwardPointerIterator +{ +public: + typedef typename Traits::Reference::LValue value_type; + typedef typename Traits::Reference::LValue reference; + typedef typename Traits::Reference::RValue const_reference; + typedef typename Traits::Reference::Pointer pointer; + + typedef ForwardPointerIterator Ancestor; + typedef ForwardPointerIterator Self; + + ForwardPointerIterator() : Ancestor() {} + ForwardPointerIterator(pointer p) : Ancestor(p) {} + + operator pointer() const { + return const_cast(Ancestor::operator->()); + } + reference operator*() const { + return const_cast(Ancestor::operator*()); + } + pointer operator->() const { + return const_cast(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 + * + * 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 +#include +#include "glib/gslist.h" +#include "glib/glist.h" + +namespace Inkscape { + +namespace Util { + +template class GSListConstIterator; +template class GSListIterator; +template class GListConstIterator; +template class GListIterator; + +template +class GSListConstIterator { +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(&_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 +class GSListIterator { +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() const { return _list; } + GSList const *list() const { return _list; } + GSList *list() { return _list; } + + const_reference operator*() const { + return *reinterpret_cast(&_list->data); + } + reference operator*() { + return *reinterpret_cast(&_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 +class GListConstIterator { +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(&_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 +class GListIterator { +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() const { + return reinterpret_cast(_list); + } + operator GSListIterator() const { + return reinterpret_cast(_list); + } + operator GListConstIterator() const { return _list; } + GList const *list() const { return _list; } + GList *list() { return _list; } + + const_reference operator*() const { + return *reinterpret_cast(&_list->data); + } + reference operator*() { return *reinterpret_cast(&_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 +#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 const &c, unsigned n_values, ...) { + bool ret = true; + va_list args; + va_start(args, n_values); + ListContainer::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 container(values, values_end); + + ListContainer::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 c_a(ARRAY_RANGE(a)); + ListContainer c_b(ARRAY_RANGE(b)); + ListContainer c_c(ARRAY_RANGE(c)); + ListContainer 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 c_a(ARRAY_RANGE(a)); + ListContainer c_b(ARRAY_RANGE(b)); + ListContainer c_c(ARRAY_RANGE(c)); + ListContainer 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 c_a(ARRAY_RANGE(a)); + ListContainer 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 filled((std::size_t)3, 2); + UTEST_ASSERT(check_values(filled, 3, 2, 2, 2)); + } + UTEST_TEST("container size") { + ListContainer empty; + UTEST_ASSERT(empty.empty()); + UTEST_ASSERT(empty.size() == 0); + int const a[] = { 1, 2, 3 }; + ListContainer 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 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 c_a(ARRAY_RANGE(a)); + ListContainer 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 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 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::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 c(ARRAY_RANGE(values)); + + c.erase(c.begin()); + UTEST_ASSERT(check_values(c, 3, 2, 3, 4)); + + ListContainer::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 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 full_list(ARRAY_RANGE(full_ary)); + ListContainer 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 + * + * 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 +#include "util/list.h" + +namespace Inkscape { + +namespace Util { + +template +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 &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::value_type value_type; + typedef List iterator; + typedef List const_iterator; + typedef typename List::reference reference; + typedef typename List::const_reference const_reference; + typedef typename List::pointer pointer; + typedef typename List::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::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 + 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 added(value); + MutableList 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 + 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 &>(j); + if ( !j || !rest(j) ) { + _tail = _head; + } + } else { + MutableList before=_before(i); + if (j) { + set_rest(before, static_cast &>(j)); + } else { + set_rest(before, MutableList()); + _tail = before; + } + } + } + void clear() { + _head = _tail = MutableList(); + } + void resize(size_type size, const_reference fill) { + MutableList before; + MutableList 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()); + _tail = before; + } else { + _head = _tail = MutableList(); + } + } + } + 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(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 added(value); + set_rest(_tail, added); + _tail = added; + } else { + _head = _tail = MutableList(value); + } + } + // we're not required to provide pop_back if we can't + // implement it efficiently + + /* additional */ + MutableList detatchList() { + MutableList list=_head; + _head = _tail = MutableList(); + return list; + } + iterator insert_after(const_iterator pos, const_reference value) { + MutableList added(value); + if (pos) { + MutableList before=static_cast &>(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 + 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 before=static_cast &>(position); + MutableList removed=rest(before); + set_rest(before, rest(removed)); + if ( removed == _tail ) { + _tail = before; + } + } + } + +private: + MutableList _head; + MutableList _tail; + + MutableList _before(const_iterator position) { + for ( MutableList iter = _head ; iter ; ++iter ) { + if ( rest(iter) == position ) { + return iter; + } + } + return MutableList(); + } + 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 before=_before(pos); + set_rest(temp._tail, static_cast &>(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 before=static_cast &>(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 + * + * 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 +#include +#include "gc-managed.h" +#include "traits/reference.h" + +namespace Inkscape { + +namespace Util { + +/// Generic ListCell for Inkscape::Util::List. +template +struct ListCell : public GC::Managed<> { + ListCell() {} + ListCell(typename Traits::Reference::RValue v, ListCell *n) + : value(v), next(n) {} + + T value; + ListCell *next; +}; + +template class List; +template class MutableList; + +template +bool is_empty(List const &list); + +template +typename List::reference first(List const &list); + +template +List const &rest(List const &list); + +template +MutableList &rest(MutableList const &list); + +template +MutableList const &set_rest(MutableList const &list, + MutableList const &rest); + +/// Helper template. +template +class List { +public: + typedef std::forward_iterator_tag iterator_category; + typedef T const value_type; + typedef std::ptrdiff_t difference_type; + typedef typename Traits::Reference::LValue reference; + typedef typename Traits::Reference::RValue const_reference; + typedef typename Traits::Reference::Pointer pointer; + + List() : _cell(NULL) {} + explicit List(const_reference value, List const &next=List()) + : _cell(new ListCell(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 *_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(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: + * + * List() + */ +template +class List : public List { +public: + typedef T value_type; + typedef typename Traits::Reference::LValue reference; + typedef typename Traits::Reference::RValue const_reference; + typedef typename Traits::Reference::Pointer pointer; + + List() : List() {} + explicit List(const_reference value, List const &next=List()) + : List(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 +class List { +public: + typedef std::forward_iterator_tag iterator_category; + typedef T &value_type; + typedef std::ptrdiff_t difference_type; + typedef typename Traits::Reference::LValue reference; + typedef typename Traits::Reference::RValue const_reference; + typedef typename Traits::Reference::Pointer pointer; + + List() : _cell(NULL) {} + List(const_reference value, List const &next=List()) + : _cell(new ListCell(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 *_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: + * + * MutableList() + */ +template +class MutableList : public List { +public: + MutableList() {} + explicit MutableList(typename List::const_reference value, + MutableList const &next=MutableList()) + : List(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 +inline List cons(typename Traits::Reference::RValue first, + List const &rest) +{ + return List(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(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() + * + * @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 +inline MutableList cons(typename Traits::Reference::RValue first, + MutableList const &rest) +{ + return MutableList(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 +inline bool is_empty(List 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 +inline typename List::reference first(List 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 +inline List const &rest(List const &list) { + return reinterpret_cast 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 +inline MutableList &rest(MutableList const &list) { + return reinterpret_cast &>(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 +inline MutableList const &set_rest(MutableList const &list, + MutableList const &rest) +{ + list._cell->next = rest._cell; + return reinterpret_cast &>(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 + * + * 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 +#include "util/list.h" + +namespace Inkscape { + +namespace Util { + +template +inline MutableList +map_list(UnaryFunction f, InputIterator start, InputIterator end) +{ + if ( start != end ) { + MutableList head(f(*start)); + MutableList tail(head); + while ( ++start != end ) { + MutableList cell(f(*start)); + set_rest(tail, cell); + tail = cell; + } + return head; + } else { + return MutableList(); + } +} + +template +inline MutableList map_list(UnaryFunction f, List list) { + return map_list(f, list, List()); +} + +template +inline List +map_list_in_place(UnaryFunction f, List start, + List end=List()) +{ + 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 + * + * 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 +inline typename Traits::ListCopy::ResultList +reverse_list(InputIterator start, InputIterator end) { + typename Traits::ListCopy::ResultList head; + while ( start != end ) { + head = cons(*start, head); + ++start; + } + return head; +} + +template +inline typename Traits::ListCopy >::ResultList +reverse_list(List const &list) { + return reverse_list(list, List()); +} + +template +inline MutableList +reverse_list_in_place(MutableList start, + MutableList end=MutableList()) +{ + MutableList reversed(end); + while ( start != end ) { + MutableList 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 + * + * Copyright (C) 2004 MenTaLguY + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include +#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 + * + * 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 +#include + +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 + * + * 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 +struct Tuple { + Tuple() {} + Tuple(typename Traits::Reference::RValue a_, + typename Traits::Reference::RValue b_, + typename Traits::Reference::RValue c_, + typename Traits::Reference::RValue d_, + typename Traits::Reference::RValue e_, + typename Traits::Reference::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 +struct Tuple { + Tuple() {} + Tuple(typename Traits::Reference::RValue a_, + typename Traits::Reference::RValue b_, + typename Traits::Reference::RValue c_, + typename Traits::Reference::RValue d_, + typename Traits::Reference::RValue e_) + : a(a_), b(b_), c(c_), d(d_), e(e_) {} + + A a; + B b; + C c; + D d; + E e; +}; + +template +struct Tuple { + Tuple() {} + Tuple(typename Traits::Reference::RValue a_, + typename Traits::Reference::RValue b_, + typename Traits::Reference::RValue c_, + typename Traits::Reference::RValue d_) + : a(a_), b(b_), c(c_), d(d_) {} + + A a; + B b; + C c; + D d; +}; + +template +struct Tuple { + Tuple() {} + Tuple(typename Traits::Reference::RValue a_, + typename Traits::Reference::RValue b_, + typename Traits::Reference::RValue c_) + : a(a_), b(b_), c(c_) {} + + A a; + B b; + C c; +}; + +template +struct Tuple { + Tuple() {} + Tuple(typename Traits::Reference::RValue a_, + typename Traits::Reference::RValue b_) + : a(a_), b(b_) {} + + A a; + B b; +}; + +template +struct Tuple { + Tuple() {} + Tuple(typename Traits::Reference::RValue a_) + : a(a_) {} + + A a; +}; + +template <> struct Tuple {}; + +template +inline Tuple +tuple(typename Traits::Reference::RValue a, + typename Traits::Reference::RValue b, + typename Traits::Reference::RValue c, + typename Traits::Reference::RValue d, + typename Traits::Reference::RValue e, + typename Traits::Reference::RValue f) +{ + return Tuple(a, b, c, d, e, f); +} + +template +inline Tuple +tuple(typename Traits::Reference::RValue a, + typename Traits::Reference::RValue b, + typename Traits::Reference::RValue c, + typename Traits::Reference::RValue d, + typename Traits::Reference::RValue e) +{ + return Tuple(a, b, c, d, e); +} + +template +inline Tuple +tuple(typename Traits::Reference::RValue a, + typename Traits::Reference::RValue b, + typename Traits::Reference::RValue c, + typename Traits::Reference::RValue d) +{ + return Tuple(a, b, c, d); +} + +template +inline Tuple +tuple(typename Traits::Reference::RValue a, + typename Traits::Reference::RValue b, + typename Traits::Reference::RValue c) +{ + return Tuple(a, b, c); +} + +template +inline Tuple +tuple(typename Traits::Reference::RValue a, + typename Traits::Reference::RValue b) +{ + return Tuple(a, b); +} + +template +inline Tuple +tuple(typename Traits::Reference::RValue a) { + return Tuple(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 . + * + * 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 +#include + +#include +#include +#include +#include // 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 + 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 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 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 specification_map; + specification_map specs; + + template + 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 + inline std::string Composition::stringify(T obj) + { + os << obj; + + std::wstring str = os.str(); + + return Glib::convert(std::string(reinterpret_cast(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 obj) + { + return obj; + } + + template <> + inline std::string + Composition::stringify(Glib::ustring obj) + { + return obj; + } + + template <> + inline std::string + Composition::stringify(const char *obj) + { + return obj; + } + + // implementation of class Composition + template + 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 + inline Glib::ustring ucompose(const Glib::ustring &fmt, const T1 &o1) + { + UStringPrivate::Composition c(fmt); + c.arg(o1); + return c.str(); + } + + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 +#endif + +#include +#include +#include + +#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 +#include + +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 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 -- cgit v1.2.3