new files from sakari, missed last time
[ardour.git] / libs / glibmm2 / glib / src / optiongroup.ccg
1 // -*- c++ -*-
2 /* $Id: optiongroup.ccg,v 1.15.4.3 2006/03/30 12:19:58 murrayc Exp $ */
3
4 /* Copyright (C) 2002 The gtkmm Development Team
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the Free
18  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20
21 #include <glibmm/optionentry.h>
22 #include <glibmm/optioncontext.h>
23 #include <glibmm/utility.h>
24 //#include <glibmm/containers.h>
25 #include <glib/gmem.h> // g_malloc
26 #include <glib/goption.h>
27
28 namespace Glib
29 {
30
31 namespace //anonymous
32 {
33
34 extern "C"
35 {
36
37 static gboolean g_callback_pre_parse(GOptionContext* context, GOptionGroup* /* group */, gpointer data, GError** /* TODO error */)
38 {
39   OptionContext cppContext(context, false /* take_ownership */);
40   //OptionGroup cppGroup(group, true /* take_copy */); //Maybe this should be option_group.
41
42   OptionGroup* option_group = static_cast<OptionGroup*>(data);
43   if(option_group)
44     return option_group->on_pre_parse(cppContext, *option_group);
45   else
46     return false;
47 }
48
49 static gboolean g_callback_post_parse(GOptionContext* context, GOptionGroup* /* group */, gpointer data, GError** /* TODO error */)
50 {
51   OptionContext cppContext(context, false /* take_ownership */);
52   //OptionGroup cppGroup(group, true /* take_copy */); //Maybe this should be option_group.
53
54   OptionGroup* option_group = static_cast<OptionGroup*>(data);
55   if(option_group)
56   {
57     return option_group->on_post_parse(cppContext, *option_group);
58   }
59   else
60     return false;
61 }
62
63 static void g_callback_error(GOptionContext* context, GOptionGroup* /* group */, gpointer data, GError** /* TODO error*/)
64 {
65   OptionContext cppContext(context, false /* take_ownership */);
66   //OptionGroup cppGroup(group); //Maybe this should be option_group.
67
68   OptionGroup* option_group = static_cast<OptionGroup*>(data);
69   if(option_group)
70     return option_group->on_error(cppContext, *option_group);
71 }
72
73 } /* extern "C" */
74
75 } //anonymous namespace
76
77
78 OptionGroup::OptionGroup(const Glib::ustring& name, const Glib::ustring& description, const Glib::ustring& help_description)
79 : gobject_( g_option_group_new(name.c_str(), description.c_str(), help_description.c_str(), this, 0 /* destroy_func */) ),
80   has_ownership_(true)
81 {
82   //Connect callbacks, so that derived classes can override the virtual methods:
83   g_option_group_set_parse_hooks(gobj(), &g_callback_pre_parse, &g_callback_post_parse);
84   g_option_group_set_error_hook(gobj(), &g_callback_error);
85 }
86
87 OptionGroup::OptionGroup(GOptionGroup* castitem)
88 : gobject_(castitem),
89   has_ownership_(true)
90 {
91   //Always takes ownership - never takes copy.
92 }
93
94
95 OptionGroup::~OptionGroup()
96 {
97   //Free any C types that were allocated during add_entry():
98   for(type_map_entries::iterator iter = map_entries_.begin(); iter != map_entries_.end(); ++iter)
99   {
100     CppOptionEntry& cpp_entry = iter->second;
101     cpp_entry.release_c_arg();
102   }
103
104   if(has_ownership_)
105   {
106     g_option_group_free(gobj());
107     gobject_ = 0;
108   }
109 }
110
111 void OptionGroup::add_entry(const OptionEntry& entry)
112 {
113   //It does not copy the entry, so it needs to live as long as the group.
114
115   //g_option_group_add_entries takes an array, with the last item in the array having a null long_name.
116   //Hopefully this will be properly documented eventually - see bug #
117
118   //Create a temporary array, just so we can give the correct thing to g_option_group_add_entries:
119   GOptionEntry array[2];
120   array[0] = *(entry.gobj()); //Copy contents.
121   GLIBMM_INITIALIZE_STRUCT(array[1], GOptionEntry);
122
123   g_option_group_add_entries(gobj(), array);
124 }
125
126 void OptionGroup::add_entry(const OptionEntry& entry, bool& arg)
127 {
128   add_entry_with_wrapper(entry, G_OPTION_ARG_NONE /* Actually a boolean on/off, depending on whether the argument name was given, without argument parameters. */, &arg);
129 }
130
131 void OptionGroup::add_entry(const OptionEntry& entry, int& arg)
132 {
133   add_entry_with_wrapper(entry, G_OPTION_ARG_INT, &arg);
134 }
135
136 void OptionGroup::add_entry(const OptionEntry& entry, Glib::ustring& arg)
137 {
138   add_entry_with_wrapper(entry, G_OPTION_ARG_STRING, &arg);
139 }
140
141 void OptionGroup::add_entry(const OptionEntry& entry, vecustrings& arg)
142 {
143   add_entry_with_wrapper(entry, G_OPTION_ARG_STRING_ARRAY, &arg);
144 }
145
146 void OptionGroup::add_entry_filename(const OptionEntry& entry, std::string& arg)
147 {
148   add_entry_with_wrapper(entry, G_OPTION_ARG_FILENAME, &arg);
149 }
150
151 void OptionGroup::add_entry_filename(const OptionEntry& entry, vecstrings& arg)
152 {
153   add_entry_with_wrapper(entry, G_OPTION_ARG_FILENAME_ARRAY, &arg);
154 }
155  
156 void OptionGroup::add_entry_with_wrapper(const OptionEntry& entry, GOptionArg arg_type, void* cpp_arg)
157 {
158   const Glib::ustring name = entry.get_long_name();
159   type_map_entries::iterator iterFind = map_entries_.find(name);
160   if( iterFind == map_entries_.end() ) //If we have not added this entry already
161   {
162     CppOptionEntry cppEntry;
163     cppEntry.carg_type_ = arg_type;
164     cppEntry.allocate_c_arg();
165     cppEntry.set_c_arg_default(cpp_arg);
166
167     cppEntry.cpparg_ = cpp_arg;
168
169     //Give the information to the C API:
170
171     cppEntry.entry_ = new OptionEntry(entry); //g_option_group_add_entry() does not take its own copy, so we must keep the instance alive. */
172     //cppEntry.entry_ is deleted in release_c_arg(), via the destructor.
173
174     cppEntry.entry_->gobj()->arg = arg_type;
175     cppEntry.entry_->gobj()->arg_data = cppEntry.carg_;
176
177     //Remember the C++/C mapping so that we can use it later:
178     map_entries_[name] = cppEntry;
179
180     add_entry(*(cppEntry.entry_));
181   }
182 }
183
184
185 bool OptionGroup::on_pre_parse(OptionContext& /* context */, OptionGroup& /* group */)
186 {
187   return true;
188 }
189
190 bool OptionGroup::on_post_parse(OptionContext& /* context */, OptionGroup& /* group */)
191 {
192   //Call this at the start of overrides.
193
194   //TODO: Maybe put this in the C callback:
195
196   //The C args have now been given values by GOption.
197   //Convert C values to C++ values:
198
199   for(type_map_entries::iterator iter = map_entries_.begin(); iter != map_entries_.end(); ++iter)
200   {
201     CppOptionEntry& cpp_entry = iter->second;
202     cpp_entry.convert_c_to_cpp();
203   }
204
205   return true;
206 }
207
208 void OptionGroup::on_error(OptionContext& /* context */, OptionGroup& /* group */)
209 {
210 }
211
212
213 OptionGroup::CppOptionEntry::CppOptionEntry()
214 : carg_type_(G_OPTION_ARG_NONE), carg_(0), cpparg_(0), entry_(0)
215 {}
216
217 void OptionGroup::CppOptionEntry::allocate_c_arg()
218 {
219   //Create an instance of the appropriate C type.
220   //This will be destroyed in the OptionGroup destructor.
221   //
222   //We must also call set_c_arg_default() to give these C types the specified defaults based on the C++-typed arguments.
223   switch(carg_type_)
224   {
225     case G_OPTION_ARG_STRING: //The char* will be for UTF8 strins.
226     case G_OPTION_ARG_FILENAME: //The char* will be for strings in the current locale's encoding.
227     {
228       char** typed_arg = new char*;
229       //The C code will allocate a char* and put it here, for us to g_free() later.
230       //Alternatively, set_c_arg_default() might allocate a char*, and the C code might or might not free and replace that.
231       *typed_arg = 0;
232       carg_ = typed_arg;
233
234       break;
235     }
236     case G_OPTION_ARG_INT:
237     {
238       int* typed_arg = new int;
239       *typed_arg = 0;
240       carg_ = typed_arg;
241
242       break;
243     }
244     case G_OPTION_ARG_STRING_ARRAY:
245     case G_OPTION_ARG_FILENAME_ARRAY:
246     {
247       char*** typed_arg = new char**;
248       *typed_arg = 0;
249       carg_ = typed_arg;
250
251       break;
252     }
253     case G_OPTION_ARG_NONE: /* Actually a boolean. */
254     {
255       gboolean* typed_arg = new gboolean;
256       *typed_arg = 0;
257       carg_ = typed_arg;
258
259       break;
260     }
261     default:
262     {
263       break;
264     }
265   }
266 }
267
268 void OptionGroup::CppOptionEntry::set_c_arg_default(void* cpp_arg)
269 {
270   switch(carg_type_)
271   {
272     case G_OPTION_ARG_INT:
273     {
274       *static_cast<int*>(carg_) = *static_cast<int*>(cpp_arg);
275       break;
276     }
277     case G_OPTION_ARG_NONE:
278     {
279       *static_cast<gboolean*>(carg_) = *static_cast<bool*>(cpp_arg);
280       break;
281     }
282     case G_OPTION_ARG_STRING:
283     {
284       Glib::ustring* typed_cpp_arg = static_cast<Glib::ustring*>(cpp_arg);
285       if(typed_cpp_arg && !typed_cpp_arg->empty())
286       {
287         const char** typed_c_arg = static_cast<const char**>(carg_);
288         *typed_c_arg = g_strdup(typed_cpp_arg->c_str()); //Freed in release_c_arg().
289       }
290       break;
291     }
292     case G_OPTION_ARG_FILENAME:
293     {
294       std::string* typed_cpp_arg = static_cast<std::string*>(cpp_arg);
295       if(typed_cpp_arg && !typed_cpp_arg->empty())
296       {
297         const char** typed_c_arg = static_cast<const char**>(carg_);
298         *typed_c_arg = g_strdup(typed_cpp_arg->c_str()); //Freed in release_c_arg().
299       }
300       break;
301     }
302     case G_OPTION_ARG_STRING_ARRAY:
303     {
304       std::vector<Glib::ustring>* typed_cpp_arg = static_cast<std::vector<Glib::ustring>*>(cpp_arg);
305       if(typed_cpp_arg)
306       {
307         std::vector<Glib::ustring>& vec = *typed_cpp_arg;
308         const char** array = static_cast<const char**>( g_malloc(sizeof(gchar*) * (vec.size() + 1)) );
309
310         for(std::vector<Glib::ustring>::size_type i = 0; i < vec.size(); ++i)
311         {
312           array[i] = g_strdup( vec[i].c_str() );
313         }
314
315         array[vec.size()] = 0;
316
317         const char*** typed_c_arg = static_cast<const char***>(carg_);
318         *typed_c_arg = array;
319       }
320       break;
321     }
322     case G_OPTION_ARG_FILENAME_ARRAY:
323     {
324       std::vector<std::string>* typed_cpp_arg = static_cast<std::vector<std::string>*>(cpp_arg);
325       if(typed_cpp_arg)
326       {
327         std::vector<std::string>& vec = *typed_cpp_arg;
328         const char** array = static_cast<const char**>( g_malloc(sizeof(gchar*) * (vec.size() + 1)) );
329
330         for(std::vector<Glib::ustring>::size_type i = 0; i < vec.size(); ++i)
331         {
332           array[i] = g_strdup( vec[i].c_str() );
333         }
334
335         array[vec.size()] = 0;
336
337         const char*** typed_c_arg = static_cast<const char***>(carg_);
338         *typed_c_arg = array;
339       }
340       break;
341     }
342     default:
343     {
344       break;
345     }
346   }
347 }
348
349 void OptionGroup::CppOptionEntry::release_c_arg()
350 {
351   //Delete the instances that we created in allocate_c_arg().
352   //Notice that we delete the type that we created, but not the value to which it points.
353   if(carg_)
354   {
355     switch(carg_type_)
356     {
357       case G_OPTION_ARG_STRING:
358       case G_OPTION_ARG_FILENAME:
359       {
360         char** typed_arg = static_cast<char**>(carg_);
361         g_free(*typed_arg); //Free the char* string at type_arg, which was allocated by the C code.
362         delete typed_arg; //Delete the char** that we allocated in allocate_c_arg;
363
364         break;
365       }
366       case G_OPTION_ARG_INT:
367       {
368         int* typed_arg = static_cast<int*>(carg_);
369         delete typed_arg;
370
371         break;
372       }
373       case G_OPTION_ARG_STRING_ARRAY:
374       case G_OPTION_ARG_FILENAME_ARRAY:
375       {
376         delete (char**)carg_;
377         break;
378       }
379       case G_OPTION_ARG_NONE: /* Actually a boolean. */
380       {
381         gboolean* typed_arg = static_cast<gboolean*>(carg_);
382         delete typed_arg;
383
384         break;
385       }
386       default:
387       {
388         /* TODO:
389         G_OPTION_ARG_CALLBACK,
390         */
391         break;
392       }
393     }
394
395     carg_ = 0;
396   }
397
398   if(entry_)
399     delete entry_;
400 }
401
402 void OptionGroup::CppOptionEntry::convert_c_to_cpp()
403 {
404   switch(carg_type_)
405   {
406     case G_OPTION_ARG_STRING:
407     {
408       char** typed_arg = static_cast<char**>(carg_);
409       Glib::ustring* typed_cpp_arg = static_cast<Glib::ustring*>(cpparg_);
410       if(typed_arg && typed_cpp_arg)
411       {
412         char* pch = *typed_arg;
413         (*typed_cpp_arg) = Glib::convert_const_gchar_ptr_to_ustring(pch);
414
415         break;
416       }
417     }
418     case G_OPTION_ARG_FILENAME:
419     {
420       char** typed_arg = static_cast<char**>(carg_);
421       std::string* typed_cpp_arg = static_cast<std::string*>(cpparg_);
422       if(typed_arg && typed_cpp_arg)
423       {
424         char* pch = *typed_arg;
425         (*typed_cpp_arg) = Glib::convert_const_gchar_ptr_to_stdstring(pch);
426
427         break;
428       }
429     }
430     case G_OPTION_ARG_INT:
431     {
432       *((int*)cpparg_) = *(static_cast<int*>(carg_));
433       break;
434     }
435         case G_OPTION_ARG_STRING_ARRAY:
436     {
437       char*** typed_arg = static_cast<char***>(carg_);
438       vecustrings* typed_cpp_arg = static_cast<vecustrings*>(cpparg_);
439       if(typed_arg && typed_cpp_arg)
440       {
441         typed_cpp_arg->clear();
442
443         //The C array seems to be null-terminated.
444         //Glib::StringArrayHandle array_handle(*typed_arg,  Glib::OWNERSHIP_NONE);
445
446         //The SUN Forte compiler complains about this:
447         // "optiongroup.cc", line 354: Error: Cannot assign Glib::ArrayHandle<Glib::ustring, 
448         // Glib::Container_Helpers::TypeTraits<Glib::ustring>> to std::vector<Glib::ustring> without 
449         // "std::vector<Glib::ustring>::operator=(const std::vector<Glib::ustring>&)";.
450         //
451         //(*typed_cpp_arg) = array_handle;
452         //
453         //And the Tru64 compiler does not even like us to instantiate the StringArrayHandle:
454         //
455         // cxx: Error: ../../glib/glibmm/containerhandle_shared.h, line 149: the operand
456         //     of a pointer dynamic_cast must be a pointer to a complete class type
457         //   return dynamic_cast<CppType>(Glib::wrap_auto(cobj, false /* take_copy */));
458
459         //for(Glib::StringArrayHandle::iterator iter = array_handle.begin(); iter != array_handle.end(); ++iter)
460         //{
461         //  typed_cpp_arg->push_back(*iter);
462         //}
463
464         //So we do this:
465
466         char** char_array_next = *typed_arg;
467         while(char_array_next && *char_array_next)
468         {
469           typed_cpp_arg->push_back(*char_array_next);
470           ++char_array_next;
471         }
472       }
473
474       break;
475     }
476     case G_OPTION_ARG_FILENAME_ARRAY:
477     {
478       char*** typed_arg = static_cast<char***>(carg_);
479       vecustrings* typed_cpp_arg = static_cast<vecustrings*>(cpparg_);
480       if(typed_arg && typed_cpp_arg)
481       { 
482         typed_cpp_arg->clear();
483
484         //See comments above about the SUN Forte and Tru64 compilers.
485
486         char** char_array_next = *typed_arg;
487         while(char_array_next && *char_array_next)
488         {
489           typed_cpp_arg->push_back(*char_array_next);
490           ++char_array_next;
491         }
492       }
493
494       break;
495     }
496     case G_OPTION_ARG_NONE: /* Actually a boolean. */
497     {
498       *(static_cast<bool*>(cpparg_)) = *(static_cast<gboolean*>(carg_));
499       break;
500     }
501     default:
502     {
503       /* TODO:
504       G_OPTION_ARG_CALLBACK,
505       */
506       break;
507     }
508   }
509
510
511 GOptionGroup* OptionGroup::gobj_give_ownership()
512 {
513   has_ownership_ = false;
514   return gobj();
515 }
516
517 } // namespace Glib
518