initial commit, lordsawar source, slightly modified
[lordsawar] / src / boxcompose.h
1 /* Defines Box::ucompose(fmt, arg...) for easy, i18n-friendly
2  * composition of labels and images with Gtkmm >= 1.3.* (see www.gtkmm.org).
3  * Uses Glib::ustring instead of std::string which doesn't work with
4  * Gtkmm due to character encoding troubles with stringstreams.
5  *
6  * Version 1.0.4.
7  *
8  * Copyright (c) 2002, 03, 04 Ole Laursen <olau@hardworking.dk>.
9  * modified from String::ucompose by Ole.
10  * Changed by Ben Asselstine to create HBoxes instead.
11  * Copyright (C) 2009 Ben Asselstine
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public License
15  * as published by the Free Software Foundation; either version 2.1 of
16  * the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful, but
19  * WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this file; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
26  * 02110-1301, USA.
27  */
28
29 //
30 // Basic usage is like
31 //
32 //   Box::ucompose("This is a %1x%2 matrix.", rows, cols);
33 //
34
35 #ifndef BOX_UCOMPOSE_H
36 #define BOX_UCOMPOSE_H
37
38 #include <glibmm/ustring.h>
39 #include <glibmm/convert.h>
40
41 #include <sstream>
42 #include <string>
43 #include <list>
44 #include <map>                  // for multimap
45
46 namespace UBoxPrivate
47 {
48   // the actual composition class - using String::ucompose is cleaner, so we
49   // hide it here
50   class Composition
51   {
52   public:
53     // initialize and prepare format string on the form "text %1 text %2 etc."
54     explicit Composition(std::string fmt);
55
56     // supply an replacement argument starting from %1
57     template <typename T>
58     Composition &arg(const T &obj);
59
60     // compose and return string
61     Gtk::HBox *box() const;
62
63   private:
64     //Glib::ustring str() const;
65     std::wostringstream os;
66     int arg_no;
67
68     // we store the output as a list - when the output string is requested, the
69     // list is concatenated to a string; this way we can keep iterators into
70     // the list instead of into a string where they're possibly invalidated
71     // when inserting a specification string
72     typedef std::list<std::string> output_list;
73     output_list output;
74
75     // the initial parse of the format string fills in the specification map
76     // with positions for each of the various %?s
77     typedef std::multimap<int, output_list::iterator> specification_map;
78     specification_map specs;
79
80     template <typename T>
81     std::string stringify(T obj);
82   };
83
84   // helper for converting spec string numbers
85   inline int char_to_int(char c)
86   {
87     switch (c) {
88     case '0': return 0;
89     case '1': return 1;
90     case '2': return 2;
91     case '3': return 3;
92     case '4': return 4;
93     case '5': return 5;
94     case '6': return 6;
95     case '7': return 7;
96     case '8': return 8;
97     case '9': return 9;
98     default: return -1000;
99     }
100   }
101
102   inline bool is_number(int n)
103   {
104     switch (n) {
105     case '0':
106     case '1':
107     case '2':
108     case '3':
109     case '4':
110     case '5':
111     case '6':
112     case '7':
113     case '8':
114     case '9':
115       return true;
116     
117     default:
118       return false;
119     }
120   }
121
122   template <typename T>
123   inline std::string Composition::stringify(T obj)
124   {
125     os << obj;
126
127     std::wstring str = os.str();
128     
129     return Glib::convert(std::string(reinterpret_cast<const char *>(str.data()),
130                                      str.size() * sizeof(wchar_t)),
131                          "UTF-8", "WCHAR_T");
132   }
133
134   // specialisations for the common string types
135   template <>
136   inline std::string
137   Composition::stringify<std::string>(std::string obj)
138   {
139     return obj;
140   }
141   
142   template <>
143   inline std::string
144   Composition::stringify<Glib::ustring>(Glib::ustring obj)
145   {
146     return obj;
147   }
148   
149   template <>
150   inline std::string
151   Composition::stringify<const char *>(const char *obj)
152   {
153     return obj;
154   }
155   
156   template <>
157   inline std::string
158   Composition::stringify<Glib::RefPtr<Gdk::Pixbuf> >(Glib::RefPtr<Gdk::Pixbuf> obj)
159   {
160     char buf[32];
161     snprintf(buf, sizeof(buf), "Gdk::Pixbuf %p", obj->gobj());
162     return buf;
163   }
164   
165   // implementation of class Composition
166   template <typename T>
167   inline Composition &Composition::arg(const T &obj)
168   {
169     Glib::ustring rep = stringify(obj);
170     
171     if (!rep.empty()) {         // manipulators don't produce output
172       for (specification_map::const_iterator i = specs.lower_bound(arg_no),
173              end = specs.upper_bound(arg_no); i != end; ++i) {
174         output_list::iterator pos = i->second;
175         ++pos;
176       
177         output.insert(pos, rep);
178       }
179     
180       os.str(std::wstring());
181       //os.clear();
182       ++arg_no;
183     }
184   
185     return *this;
186   }
187
188   inline Composition::Composition(std::string fmt)
189     : arg_no(1)
190   {
191 //#if __GNUC__ >= 3
192     //os.imbue(std::locale("")); // use the user's locale for the stream
193 //#endif
194     std::string::size_type b = 0, i = 0;
195   
196     // fill in output with the strings between the %1 %2 %3 etc. and
197     // fill in specs with the positions
198     while (i < fmt.length()) {
199       if (fmt[i] == '%' && i + 1 < fmt.length()) {
200         if (fmt[i + 1] == '%') { // catch %%
201           fmt.replace(i, 2, "%");
202           ++i;
203         }
204         else if (is_number(fmt[i + 1])) { // aha! a spec!
205           // save string
206           output.push_back(fmt.substr(b, i - b));
207         
208           int n = 1;            // number of digits
209           int spec_no = 0;
210
211           do {
212             spec_no += char_to_int(fmt[i + n]);
213             spec_no *= 10;
214             ++n;
215           } while (i + n < fmt.length() && is_number(fmt[i + n]));
216
217           spec_no /= 10;
218           output_list::iterator pos = output.end();
219           --pos;                // safe since we have just inserted a string
220         
221           specs.insert(specification_map::value_type(spec_no, pos));
222         
223           // jump over spec string
224           i += n;
225           b = i;
226         }
227         else
228           ++i;
229       }
230       else
231         ++i;
232     }
233   
234     if (i - b > 0)              // add the rest of the string
235       output.push_back(fmt.substr(b, i - b));
236   }
237
238   //inline Glib::ustring Composition::str() const
239   //{
240     // assemble string
241     //std::string str;
242   
243     //for (output_list::const_iterator i = output.begin(), end = output.end();
244          //i != end; ++i)
245       //str += *i;
246   
247     //return str;
248   //}
249   inline Gtk::HBox* Composition::box() const
250   {
251     Gtk::HBox *hbox = new Gtk::HBox();
252     for (output_list::const_iterator i = output.begin(), end = output.end();
253          i != end; ++i)
254       {
255         Gtk::Label *label = new Gtk::Label(*i);
256
257         void *ptr = NULL;
258         int retval = sscanf (label->get_text().c_str(), "Gdk::Pixbuf %p", &ptr);
259         if (retval != 1)
260           hbox->pack_start(*manage(label), Gtk::PACK_SHRINK, 0);
261         else
262           {
263             Glib::RefPtr<Gdk::Pixbuf> pic = Glib::wrap((GdkPixbuf*)ptr, true);
264             Gtk::Image *image = new Gtk::Image();
265             image->property_pixbuf() = pic;
266             hbox->pack_start(*manage(image), Gtk::PACK_SHRINK, 0);
267           }
268       }
269       return hbox;
270   }
271 }
272
273
274 namespace Box
275 {
276   // a series of functions which accept a format string on the form "text %1
277   // more %2 less %3" and a number of templated parameters and spits out the
278   // composited string
279   template <typename T1>
280   inline Gtk::HBox* ucompose(const Glib::ustring &fmt, const T1 &o1)
281   {
282     UBoxPrivate::Composition c(fmt);
283     c.arg(o1);
284     return c.box();
285   }
286
287   template <typename T1, typename T2>
288   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
289                                 const T1 &o1, const T2 &o2)
290   {
291     UBoxPrivate::Composition c(fmt);
292     c.arg(o1).arg(o2);
293     return c.box();
294   }
295
296   template <typename T1, typename T2, typename T3>
297   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
298                                 const T1 &o1, const T2 &o2, const T3 &o3)
299   {
300     UBoxPrivate::Composition c(fmt);
301     c.arg(o1).arg(o2).arg(o3);
302     return c.box();
303   }
304
305   template <typename T1, typename T2, typename T3, typename T4>
306   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
307                                 const T1 &o1, const T2 &o2, const T3 &o3,
308                                 const T4 &o4)
309   {
310     UBoxPrivate::Composition c(fmt);
311     c.arg(o1).arg(o2).arg(o3).arg(o4);
312     return c.box();
313   }
314
315   template <typename T1, typename T2, typename T3, typename T4, typename T5>
316   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
317                                 const T1 &o1, const T2 &o2, const T3 &o3,
318                                 const T4 &o4, const T5 &o5)
319   {
320     UBoxPrivate::Composition c(fmt);
321     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
322     return c.box();
323   }
324
325   template <typename T1, typename T2, typename T3, typename T4, typename T5,
326             typename T6>
327   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
328                                 const T1 &o1, const T2 &o2, const T3 &o3,
329                                 const T4 &o4, const T5 &o5, const T6 &o6)
330   {
331     UBoxPrivate::Composition c(fmt);
332     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
333     return c.box();
334   }
335
336   template <typename T1, typename T2, typename T3, typename T4, typename T5,
337             typename T6, typename T7>
338   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
339                                 const T1 &o1, const T2 &o2, const T3 &o3,
340                                 const T4 &o4, const T5 &o5, const T6 &o6,
341                                 const T7 &o7)
342   {
343     UBoxPrivate::Composition c(fmt);
344     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
345     return c.box();
346   }
347
348   template <typename T1, typename T2, typename T3, typename T4, typename T5,
349             typename T6, typename T7, typename T8>
350   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
351                                 const T1 &o1, const T2 &o2, const T3 &o3,
352                                 const T4 &o4, const T5 &o5, const T6 &o6,
353                                 const T7 &o7, const T8 &o8)
354   {
355     UBoxPrivate::Composition c(fmt);
356     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
357     return c.box();
358   }
359
360   template <typename T1, typename T2, typename T3, typename T4, typename T5,
361             typename T6, typename T7, typename T8, typename T9>
362   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
363                                 const T1 &o1, const T2 &o2, const T3 &o3,
364                                 const T4 &o4, const T5 &o5, const T6 &o6,
365                                 const T7 &o7, const T8 &o8, const T9 &o9)
366   {
367     UBoxPrivate::Composition c(fmt);
368     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
369     return c.box();
370   }
371
372   template <typename T1, typename T2, typename T3, typename T4, typename T5,
373             typename T6, typename T7, typename T8, typename T9, typename T10>
374   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
375                                 const T1 &o1, const T2 &o2, const T3 &o3,
376                                 const T4 &o4, const T5 &o5, const T6 &o6,
377                                 const T7 &o7, const T8 &o8, const T9 &o9,
378                                 const T10 &o10)
379   {
380     UBoxPrivate::Composition c(fmt);
381     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
382       .arg(o10);
383     return c.box();
384   }
385   
386   template <typename T1, typename T2, typename T3, typename T4, typename T5,
387             typename T6, typename T7, typename T8, typename T9, typename T10,
388             typename T11>
389   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
390                                 const T1 &o1, const T2 &o2, const T3 &o3,
391                                 const T4 &o4, const T5 &o5, const T6 &o6,
392                                 const T7 &o7, const T8 &o8, const T9 &o9,
393                                 const T10 &o10, const T11 &o11)
394   {
395     UBoxPrivate::Composition c(fmt);
396     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
397       .arg(o10).arg(o11);
398     return c.box();
399   }
400
401   template <typename T1, typename T2, typename T3, typename T4, typename T5,
402             typename T6, typename T7, typename T8, typename T9, typename T10,
403             typename T11, typename T12>
404   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
405                                 const T1 &o1, const T2 &o2, const T3 &o3,
406                                 const T4 &o4, const T5 &o5, const T6 &o6,
407                                 const T7 &o7, const T8 &o8, const T9 &o9,
408                                 const T10 &o10, const T11 &o11, const T12 &o12)
409   {
410     UBoxPrivate::Composition c(fmt);
411     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
412       .arg(o10).arg(o11).arg(o12);
413     return c.box();
414   }
415
416   template <typename T1, typename T2, typename T3, typename T4, typename T5,
417             typename T6, typename T7, typename T8, typename T9, typename T10,
418             typename T11, typename T12, typename T13>
419   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
420                                 const T1 &o1, const T2 &o2, const T3 &o3,
421                                 const T4 &o4, const T5 &o5, const T6 &o6,
422                                 const T7 &o7, const T8 &o8, const T9 &o9,
423                                 const T10 &o10, const T11 &o11, const T12 &o12,
424                                 const T13 &o13)
425   {
426     UBoxPrivate::Composition c(fmt);
427     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
428       .arg(o10).arg(o11).arg(o12).arg(o13);
429     return c.box();
430   }
431
432   template <typename T1, typename T2, typename T3, typename T4, typename T5,
433             typename T6, typename T7, typename T8, typename T9, typename T10,
434             typename T11, typename T12, typename T13, typename T14>
435   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
436                                 const T1 &o1, const T2 &o2, const T3 &o3,
437                                 const T4 &o4, const T5 &o5, const T6 &o6,
438                                 const T7 &o7, const T8 &o8, const T9 &o9,
439                                 const T10 &o10, const T11 &o11, const T12 &o12,
440                                 const T13 &o13, const T14 &o14)
441   {
442     UBoxPrivate::Composition c(fmt);
443     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
444       .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
445     return c.box();
446   }
447
448   template <typename T1, typename T2, typename T3, typename T4, typename T5,
449             typename T6, typename T7, typename T8, typename T9, typename T10,
450             typename T11, typename T12, typename T13, typename T14,
451             typename T15>
452   inline Gtk::HBox* ucompose(const Glib::ustring &fmt,
453                                 const T1 &o1, const T2 &o2, const T3 &o3,
454                                 const T4 &o4, const T5 &o5, const T6 &o6,
455                                 const T7 &o7, const T8 &o8, const T9 &o9,
456                                 const T10 &o10, const T11 &o11, const T12 &o12,
457                                 const T13 &o13, const T14 &o14, const T15 &o15)
458   {
459     UBoxPrivate::Composition c(fmt);
460     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
461       .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
462     return c.box();
463   }
464 }
465
466
467 #endif // BOX_UCOMPOSE_HPP