Code

Fixed crash when draw height was zero.
[inkscape.git] / src / extension / paramnotebook.cpp
1 /** \file
2  * Notebook and NotebookPage parameters for extensions.
3  */
5 /*
6  * Author:
7  *   Johan Engelen <johan@shouraizou.nl>
8  *
9  * Copyright (C) 2006 Author
10  *
11  * Released under GNU GPL, read the file 'COPYING' for more information
12  */
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
19 #include <gtkmm/adjustment.h>
20 #include <gtkmm/box.h>
21 #include <gtkmm/spinbutton.h>
22 #include <gtkmm/notebook.h>
23 #include <gtkmm/tooltips.h>
25 #include <glibmm/i18n.h>
27 #include <xml/node.h>
29 #include "extension.h"
30 #include "prefs-utils.h"
31 #include "document-private.h"
32 #include "sp-object.h"
34 #include "paramnotebook.h"
36 /** \brief  The root directory in the preferences database for extension
37             related parameters. */
38 #define PREF_DIR "extensions"
40 namespace Inkscape {
41 namespace Extension {
44 // \brief  A class to represent the pages of a notebookparameter of an extension
45 class ParamNotebookPage : public Parameter {
46 private:
47     GSList * parameters; /**< A table to store the parameters for this page.
48                               This only gets created if there are parameters on this
49                               page */
50     Gtk::Tooltips * _tooltips;
51     
52 public:
53     static ParamNotebookPage * makepage (Inkscape::XML::Node * in_repr, Inkscape::Extension::Extension * in_ext);
55     ParamNotebookPage(const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml);
56     ~ParamNotebookPage(void);
57     Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node);
58     Glib::ustring * paramString (void);
59     gchar * get_guitext (void) {return _text;};
60     
61 }; /* class ParamNotebookPage */
64 ParamNotebookPage::ParamNotebookPage (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) :
65     Parameter(name, guitext, desc, scope, ext)
66 {
67     parameters = NULL;
68         
69     // Read XML to build page
70     if (xml != NULL) {
71         Inkscape::XML::Node *child_repr = sp_repr_children(xml);
72         while (child_repr != NULL) {
73             char const * chname = child_repr->name();
74             if (chname[0] == '_') // Allow _ for translation of tags
75                 chname++;
76             if (!strcmp(chname, "param") || !strcmp(chname, "_param")) {
77                 Parameter * param;
78                 param = Parameter::make(child_repr, ext);
79                 if (param != NULL) parameters = g_slist_append(parameters, param);
80             }
81             child_repr = sp_repr_next(child_repr);
82         }
83     }
84     
85     return;
86 }
88 ParamNotebookPage::~ParamNotebookPage (void)
89 {
90     if (_tooltips) delete _tooltips;
91     //destroy parameters
92     for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
93         Parameter * param = reinterpret_cast<Parameter *>(list->data);
94         delete param;
95     }
96     g_slist_free(parameters);
97 }
99 /** \brief  Return the value as a string */
100 Glib::ustring *
101 ParamNotebookPage::paramString (void)
103     Glib::ustring * param_string = new Glib::ustring("");
105     for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
106         Parameter * param = reinterpret_cast<Parameter *>(list->data);
108         *param_string += " --";
109         *param_string += param->name();
110         *param_string += "=";
111         Glib::ustring * paramstr = param->string();
112         *param_string += *paramstr;
113         delete paramstr;
114     }
116     return param_string;
120 /**
121     \return None
122     \brief  This function creates a page that can be used later.  This
123             is typically done in the creation of the notebook and defined
124             in the XML file describing the extension (it's private so people
125             have to use the system) :)
126     \param  in_repr  The XML describing the page
128     This function first grabs all of the data out of the Repr and puts
129     it into local variables.  Actually, these are just pointers, and the
130     data is not duplicated so we need to be careful with it.  If there
131     isn't a name in the XML, then no page is created as
132     the function just returns.
134     From this point on, we're pretty committed as we've allocated an
135     object and we're starting to fill it.  The name is set first, and
136     is created with a strdup to actually allocate memory for it.  Then
137     there is a case statement (roughly because strcmp requires 'ifs')
138     based on what type of parameter this is.  Depending which type it
139     is, the value is interpreted differently, but they are relatively
140     straight forward.  In all cases the value is set to the default
141     value from the XML and the type is set to the interpreted type.
142 */
143 ParamNotebookPage *
144 ParamNotebookPage::makepage (Inkscape::XML::Node * in_repr, Inkscape::Extension::Extension * in_ext)
146     const char * name;
147     const char * guitext;
148     const char * desc;
149     const char * scope_str;
150     Parameter::_scope_t scope = Parameter::SCOPE_USER;
152     name = in_repr->attribute("name");
153     guitext = in_repr->attribute("gui-text");
154     if (guitext == NULL)
155         guitext = in_repr->attribute("_gui-text");
156     desc = in_repr->attribute("gui-description");
157     if (desc == NULL)
158         desc = in_repr->attribute("_gui-description");
159     scope_str = in_repr->attribute("scope");
161     /* In this case we just don't have enough information */
162     if (name == NULL) {
163         return NULL;
164     }
166     if (scope_str != NULL) {
167         if (!strcmp(scope_str, "user")) {
168             scope = Parameter::SCOPE_USER;
169         } else if (!strcmp(scope_str, "document")) {
170             scope = Parameter::SCOPE_DOCUMENT;
171         } else if (!strcmp(scope_str, "node")) {
172             scope = Parameter::SCOPE_NODE;
173         }
174     }
176     ParamNotebookPage * page = new ParamNotebookPage(name, guitext, desc, scope, in_ext, in_repr);
177     
178     /* Note: page could equal NULL */
179     return page;
184 /**
185     \brief  Creates a notebookpage widget for a notebook
187     Builds a notebook page (a vbox) and puts parameters on it.
188 */
189 Gtk::Widget *
190 ParamNotebookPage::get_widget (SPDocument * doc, Inkscape::XML::Node * node)
191 {                      
192     if (!_tooltips) _tooltips = new Gtk::Tooltips();
193     
194     Gtk::VBox * vbox = Gtk::manage(new Gtk::VBox);
195     vbox->set_border_width(5);  
196     
197     // add parameters onto page (if any)    
198     for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
199         Parameter * param = reinterpret_cast<Parameter *>(list->data);
200         Gtk::Widget * widg = param->get_widget(doc, node);
201         gchar const * tip = param->get_tooltip();
202         
203         vbox->pack_start(*widg, true, true, 2);
204         if (tip != NULL) {
205             _tooltips->set_tip(*widg, Glib::ustring(tip));
206         }
207     }
208         
209     vbox->show();
210     
211     return dynamic_cast<Gtk::Widget *>(vbox);
222 ParamNotebook::ParamNotebook (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) :
223     Parameter(name, guitext, desc, scope, ext)
225     pages = NULL;
226     
227     // Read XML tree to add pages:
228     if (xml != NULL) {
229         Inkscape::XML::Node *child_repr = sp_repr_children(xml);
230         while (child_repr != NULL) {
231             char const * chname = child_repr->name();
232             if (chname[0] == '_') // Allow _ for translation of tags
233                 chname++;
234             if (!strcmp(chname, "page")) {
235                 ParamNotebookPage * page;
236                 page = ParamNotebookPage::makepage(child_repr, ext);
237                 if (page != NULL) pages = g_slist_append(pages, page);
238             }
239             child_repr = sp_repr_next(child_repr);
240         }
241     }
242     
243     // Initialize _value with the current page
244     const char * defaultval = NULL;
245     // set first page as default
246     if (pages != NULL) {
247         ParamNotebookPage * defpage = reinterpret_cast<ParamNotebookPage *>(pages->data);
248         defaultval = defpage->name();
249     }
251     gchar * pref_name = this->pref_name();
252     const gchar * paramval = prefs_get_string_attribute(PREF_DIR, pref_name);
253     g_free(pref_name);
255     if (paramval != NULL)
256         defaultval = paramval;
257     if (defaultval != NULL)
258         _value = g_strdup(defaultval);  // allocate space for _value
259         
260     return;
263 ParamNotebook::~ParamNotebook (void)
265     //destroy pages
266     for (GSList * list = pages; list != NULL; list = g_slist_next(list)) {
267         ParamNotebookPage * page = reinterpret_cast<ParamNotebookPage *>(list->data);
268         delete page;
269     }
270     g_slist_free(pages);
272     g_free(_value);
276 /** \brief  A function to set the \c _value
277     \param  in   The number of the page which value must be set
278     \param  doc  A document that should be used to set the value.
279     \param  node The node where the value may be placed
281     This function sets the internal value, but it also sets the value
282     in the preferences structure.  To put it in the right place, \c PREF_DIR
283     and \c pref_name() are used.
285     To copy the data into _value the old memory must be free'd first.
286     It is important to note that \c g_free handles \c NULL just fine.  Then
287     the passed in value is duplicated using \c g_strdup().
288 */
289 const gchar *
290 ParamNotebook::set (const int in, SPDocument * doc, Inkscape::XML::Node * node)
292     ParamNotebookPage * page = NULL;
293     int i = 0;
294     for (GSList * list = pages; (list != NULL) && (i <= in); list = g_slist_next(list)) {
295         page = reinterpret_cast<ParamNotebookPage *>(list->data);
296         i++;
297     }
298     
299     if (page == NULL) return _value;
300    
301     if (_value != NULL) g_free(_value);
302     _value = g_strdup(page->name());
304     gchar * prefname = this->pref_name();
305     prefs_set_string_attribute(PREF_DIR, prefname, _value);
306     g_free(prefname);
308     return _value;
312 /**
313     \brief  A function to get the currentpage and the parameters in a string form
314     \return A string with the 'value' and all the parameters on all pages as command line arguments
316     This is really a hack. The function is called by Extension::paramString() to build
317     the commandline string like: '--param1name=\"param1value\" --param2name=\"param2value\" ...'
318     Extension::paramString expects this function to return '\"param1value\"'; but instead, 
319     this function returns: '\"param1value\" --page1param1name=\"page1param1value\" ...'
321     \TODO  Do this better. For example, make Parameter::paramString() that returns '--name=\"value\"'
322     instead of just returning '\"value\"'.
323 */
324 Glib::ustring *
325 ParamNotebook::string (void)
327     Glib::ustring * param_string = new Glib::ustring("");
329     *param_string += "\"";
330     *param_string += _value;  // the name of the current page
331     *param_string += "\"";
333     for (GSList * list = pages; list != NULL; list = g_slist_next(list)) {
334         ParamNotebookPage * page = reinterpret_cast<ParamNotebookPage *>(list->data);
336         Glib::ustring * pageparamstr = page->paramString();
337         *param_string += *pageparamstr;
338         delete pageparamstr;
339     }
341     return param_string;
344 /** \brief  A special category of Gtk::Notebook to handle notebook parameters */
345 class ParamNotebookWdg : public Gtk::Notebook {
346 private:
347     ParamNotebook * _pref;
348     SPDocument * _doc;
349     Inkscape::XML::Node * _node;
350 public:
351     /** \brief  Build a notebookpage preference for the given parameter
352         \param  pref  Where to get the string (pagename) from, and where to put it
353                       when it changes.
354     */
355     ParamNotebookWdg (ParamNotebook * pref, SPDocument * doc, Inkscape::XML::Node * node) :
356         Gtk::Notebook(), _pref(pref), _doc(doc), _node(node), activated(false) {
357         // don't have to set the correct page: this is done in ParamNotebook::get_widget.
358         // hook function
359         this->signal_switch_page().connect(sigc::mem_fun(this, &ParamNotebookWdg::changed_page));
360         return;
361     };
362     void changed_page(GtkNotebookPage *page, guint pagenum);
363     bool activated;
364 };
366 /** \brief  Respond to the selected page of notebook changing
367     This function responds to the changing by reporting it to
368     ParamNotebook. The change is only reported when the notebook
369     is actually visible. This to exclude 'fake' changes when the
370     notebookpages are added or removed.
371 */
372 void
373 ParamNotebookWdg::changed_page(GtkNotebookPage *page,
374                                    guint pagenum)
376     if (is_visible()) {
377         _pref->set((int)pagenum, _doc, _node);
378     }
379     return;
384 /**
385     \brief  Creates a Notebook widget for a notebook parameter
387     Builds a notebook and puts pages in it.
388 */
389 Gtk::Widget *
390 ParamNotebook::get_widget (SPDocument * doc, Inkscape::XML::Node * node)
392     ParamNotebookWdg * nb = Gtk::manage(new ParamNotebookWdg(this, doc, node));
394     // add pages (if any)    
395     int i = -1;
396     int pagenr = i;
397     for (GSList * list = pages; list != NULL; list = g_slist_next(list)) {
398         i++;  
399         ParamNotebookPage * page = reinterpret_cast<ParamNotebookPage *>(list->data);
400         Gtk::Widget * widg = page->get_widget(doc, node);
401         nb->append_page(*widg, _(page->get_guitext()));
402         if (!strcmp(_value, page->name())) {
403             pagenr = i; // this is the page to be displayed?
404         }
405     }
407     nb->show();
408     
409     if (pagenr >= 0) nb->set_current_page(pagenr);
411     return dynamic_cast<Gtk::Widget *>(nb);
415 }  /* namespace Extension */
416 }  /* namespace Inkscape */
418 /*
419   Local Variables:
420   mode:c++
421   c-file-style:"stroustrup"
422   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
423   indent-tabs-mode:nil
424   fill-column:99
425   End:
426 */
427 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :