1 /** \file
2 * extension parameter for radiobuttons.
3 *
4 * It uses a Gtk:ComboBoxText widget in the extension UI.
5 */
7 /*
8 * Author:
9 * Johan Engelen <johan@shouraizou.nl>
10 *
11 * Copyright (C) 2006-2007 Johan Engelen
12 * Copyright (C) 2008 Jon A. Cruz
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
22 #include <gtkmm/box.h>
23 #include <gtkmm/comboboxtext.h>
24 #include <gtkmm/radiobutton.h>
25 #include <gtkmm/radiobuttongroup.h>
26 #include <gtkmm/tooltips.h>
27 #include <gtkmm/label.h>
28 #include <glibmm/i18n.h>
30 #include "xml/node.h"
31 #include "extension/extension.h"
32 #include "preferences.h"
33 #include "document-private.h"
34 #include "sp-object.h"
36 #include "radiobutton.h"
38 /** \brief The root directory in the preferences database for extension
39 related parameters. */
40 #define PREF_DIR "extensions"
42 namespace Inkscape {
43 namespace Extension {
45 /* For internal use only.
46 Note that value and guitext MUST be non-NULL. This is ensured by newing only at one location in the code where non-NULL checks are made. */
47 class optionentry {
48 public:
49 optionentry (Glib::ustring * val, Glib::ustring * text) {
50 value = val;
51 guitext = text;
52 }
53 ~optionentry() {
54 delete value;
55 delete guitext;
56 }
58 Glib::ustring * value;
59 Glib::ustring * guitext;
60 };
62 ParamRadioButton::ParamRadioButton (const gchar * name,
63 const gchar * guitext,
64 const gchar * desc,
65 const Parameter::_scope_t scope,
66 bool gui_hidden,
67 const gchar * gui_tip,
68 Inkscape::Extension::Extension * ext,
69 Inkscape::XML::Node * xml,
70 AppearanceMode mode) :
71 Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext),
72 _value(0),
73 _mode(mode),
74 choices(0)
75 {
76 // Read XML tree to add enumeration items:
77 // printf("Extension Constructor: ");
78 if (xml != NULL) {
79 Inkscape::XML::Node *child_repr = sp_repr_children(xml);
80 while (child_repr != NULL) {
81 char const * chname = child_repr->name();
82 if (!strcmp(chname, INKSCAPE_EXTENSION_NS "option") || !strcmp(chname, INKSCAPE_EXTENSION_NS "_option")) {
83 Glib::ustring * newguitext = NULL;
84 Glib::ustring * newvalue = NULL;
85 const char * contents = sp_repr_children(child_repr)->content();
87 if (contents != NULL)
88 // don't translate when 'option' but do translate when '_option'
89 newguitext = new Glib::ustring( !strcmp(chname, INKSCAPE_EXTENSION_NS "_option") ? _(contents) : contents );
90 else
91 continue;
93 const char * val = child_repr->attribute("value");
94 if (val != NULL)
95 newvalue = new Glib::ustring(val);
96 else
97 newvalue = new Glib::ustring(contents);
99 if ( (newguitext) && (newvalue) ) { // logical error if this is not true here
100 choices = g_slist_append( choices, new optionentry(newvalue, newguitext) );
101 }
102 }
103 child_repr = sp_repr_next(child_repr);
104 }
105 }
107 // Initialize _value with the default value from xml
108 // for simplicity : default to the contents of the first xml-child
109 const char * defaultval = NULL;
110 if (choices)
111 defaultval = ((optionentry*) choices->data)->value->c_str();
113 gchar * pref_name = this->pref_name();
114 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
115 Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name);
116 g_free(pref_name);
118 if (!paramval.empty())
119 defaultval = paramval.data();
120 if (defaultval != NULL)
121 _value = g_strdup(defaultval); // allocate space for _value
123 return;
124 }
126 ParamRadioButton::~ParamRadioButton (void)
127 {
128 //destroy choice strings
129 for (GSList * list = choices; list != NULL; list = g_slist_next(list)) {
130 delete (reinterpret_cast<optionentry *>(list->data));
131 }
132 g_slist_free(choices);
134 g_free(_value);
135 }
138 /** \brief A function to set the \c _value
139 \param in The value to set
140 \param doc A document that should be used to set the value.
141 \param node The node where the value may be placed
143 This function sets ONLY the internal value, but it also sets the value
144 in the preferences structure. To put it in the right place, \c PREF_DIR
145 and \c pref_name() are used.
147 To copy the data into _value the old memory must be free'd first.
148 It is important to note that \c g_free handles \c NULL just fine. Then
149 the passed in value is duplicated using \c g_strdup().
150 */
151 const gchar *
152 ParamRadioButton::set (const gchar * in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/)
153 {
154 if (in == NULL) return NULL; /* Can't have NULL string */
156 Glib::ustring * settext = NULL;
157 for (GSList * list = choices; list != NULL; list = g_slist_next(list)) {
158 optionentry * entr = reinterpret_cast<optionentry *>(list->data);
159 if ( !entr->guitext->compare(in) ) {
160 settext = entr->value;
161 break; // break out of for loop
162 }
163 }
164 if (settext) {
165 if (_value != NULL) g_free(_value);
166 _value = g_strdup(settext->c_str());
167 gchar * prefname = this->pref_name();
168 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
169 prefs->setString(extension_pref_root + prefname, _value);
170 g_free(prefname);
171 }
173 return _value;
174 }
177 /**
178 \brief A function to get the current value of the parameter in a string form
179 \return A string with the 'value' as command line argument
180 */
181 void
182 ParamRadioButton::string (std::string &string)
183 {
184 string += _value;
185 return;
186 }
188 /** \brief A special radiobutton class to use in ParamRadioButton */
189 class ParamRadioButtonWdg : public Gtk::RadioButton {
190 private:
191 ParamRadioButton * _pref;
192 SPDocument * _doc;
193 Inkscape::XML::Node * _node;
194 sigc::signal<void> * _changeSignal;
195 public:
196 /** \brief Build a string preference for the given parameter
197 \param pref Where to put the radiobutton's string when it is selected.
198 */
199 ParamRadioButtonWdg ( Gtk::RadioButtonGroup& group, const Glib::ustring& label,
200 ParamRadioButton * pref, SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal ) :
201 Gtk::RadioButton(group, label), _pref(pref), _doc(doc), _node(node), _changeSignal(changeSignal) {
202 add_changesignal();
203 };
204 ParamRadioButtonWdg ( const Glib::ustring& label,
205 ParamRadioButton * pref, SPDocument * doc, Inkscape::XML::Node * node , sigc::signal<void> * changeSignal) :
206 Gtk::RadioButton(label), _pref(pref), _doc(doc), _node(node), _changeSignal(changeSignal) {
207 add_changesignal();
208 };
209 void add_changesignal() {
210 this->signal_toggled().connect(sigc::mem_fun(this, &ParamRadioButtonWdg::changed));
211 };
212 void changed (void);
213 };
215 /** \brief Respond to the selected radiobutton changing
217 This function responds to the radiobutton selection changing by grabbing the value
218 from the text box and putting it in the parameter.
219 */
220 void
221 ParamRadioButtonWdg::changed (void)
222 {
223 if (this->get_active()) {
224 Glib::ustring data = this->get_label();
225 _pref->set(data.c_str(), _doc, _node);
226 }
227 if (_changeSignal != NULL) {
228 _changeSignal->emit();
229 }
230 }
233 class ComboWdg : public Gtk::ComboBoxText {
234 public:
235 ComboWdg(ParamRadioButton* base, SPDocument * doc, Inkscape::XML::Node * node) :
236 Gtk::ComboBoxText(),
237 base(base),
238 doc(doc),
239 node(node)
240 {
241 }
242 virtual ~ComboWdg() {}
244 protected:
245 ParamRadioButton* base;
246 SPDocument* doc;
247 Inkscape::XML::Node* node;
249 virtual void on_changed() {
250 if ( base ) {
251 base->set(get_active_text().c_str(), doc, node);
252 }
253 }
254 };
256 /**
257 \brief Creates a combobox widget for an enumeration parameter
258 */
259 Gtk::Widget *
260 ParamRadioButton::get_widget (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
261 {
262 if (_gui_hidden) return NULL;
264 Gtk::HBox * hbox = Gtk::manage(new Gtk::HBox(false, 4));
265 Gtk::VBox * vbox = Gtk::manage(new Gtk::VBox(false, 0));
267 Gtk::Label * label = Gtk::manage(new Gtk::Label(_(_text), Gtk::ALIGN_LEFT, Gtk::ALIGN_TOP));
268 label->show();
269 hbox->pack_start(*label, false, false);
271 Gtk::ComboBoxText* cbt = 0;
272 bool comboSet = false;
273 if (_mode == MINIMAL) {
274 cbt = Gtk::manage(new ComboWdg(this, doc, node));
275 cbt->show();
276 vbox->pack_start(*cbt, false, false);
277 }
279 // add choice strings as radiobuttons
280 // and select last selected option (_value)
281 Gtk::RadioButtonGroup group;
282 for (GSList * list = choices; list != NULL; list = g_slist_next(list)) {
283 optionentry * entr = reinterpret_cast<optionentry *>(list->data);
284 Glib::ustring * text = entr->guitext;
285 switch ( _mode ) {
286 case MINIMAL:
287 {
288 cbt->append_text(*text);
289 if (!entr->value->compare(_value)) {
290 cbt->set_active_text(*text);
291 comboSet = true;
292 }
293 }
294 break;
295 case COMPACT:
296 case FULL:
297 {
298 ParamRadioButtonWdg * radio = Gtk::manage(new ParamRadioButtonWdg(group, *text, this, doc, node, changeSignal));
299 radio->show();
300 vbox->pack_start(*radio, true, true);
301 if (!entr->value->compare(_value)) {
302 radio->set_active();
303 }
304 }
305 break;
306 }
307 }
309 if ( (_mode == MINIMAL) && !comboSet) {
310 cbt->set_active(0);
311 }
313 vbox->show();
314 hbox->pack_end(*vbox, false, false);
315 hbox->show();
318 return dynamic_cast<Gtk::Widget *>(hbox);
319 }
322 } /* namespace Extension */
323 } /* namespace Inkscape */
325 /*
326 Local Variables:
327 mode:c++
328 c-file-style:"stroustrup"
329 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
330 indent-tabs-mode:nil
331 fill-column:99
332 End:
333 */
334 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :