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/extension.h>
30 #include "prefs-utils.h"
31 #include "document-private.h"
32 #include "sp-object.h"
34 #include "notebook.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;
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, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml);
56 ~ParamNotebookPage(void);
57 Gtk::Widget * get_widget(SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal);
58 void paramString (std::list <std::string> &list);
59 gchar * get_guitext (void) {return _text;};
61 }; /* class ParamNotebookPage */
64 ParamNotebookPage::ParamNotebookPage (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) :
65 Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext)
66 {
67 parameters = NULL;
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 }
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 void
101 ParamNotebookPage::paramString (std::list <std::string> &list)
102 {
103 for (GSList * plist = parameters; plist != NULL; plist = g_slist_next(plist)) {
104 Parameter * param = reinterpret_cast<Parameter *>(plist->data);
105 param->string(list);
106 }
108 return;
109 }
112 /**
113 \return None
114 \brief This function creates a page that can be used later. This
115 is typically done in the creation of the notebook and defined
116 in the XML file describing the extension (it's private so people
117 have to use the system) :)
118 \param in_repr The XML describing the page
120 This function first grabs all of the data out of the Repr and puts
121 it into local variables. Actually, these are just pointers, and the
122 data is not duplicated so we need to be careful with it. If there
123 isn't a name in the XML, then no page is created as
124 the function just returns.
126 From this point on, we're pretty committed as we've allocated an
127 object and we're starting to fill it. The name is set first, and
128 is created with a strdup to actually allocate memory for it. Then
129 there is a case statement (roughly because strcmp requires 'ifs')
130 based on what type of parameter this is. Depending which type it
131 is, the value is interpreted differently, but they are relatively
132 straight forward. In all cases the value is set to the default
133 value from the XML and the type is set to the interpreted type.
134 */
135 ParamNotebookPage *
136 ParamNotebookPage::makepage (Inkscape::XML::Node * in_repr, Inkscape::Extension::Extension * in_ext)
137 {
138 const char * name;
139 const char * guitext;
140 const char * desc;
141 const char * scope_str;
142 Parameter::_scope_t scope = Parameter::SCOPE_USER;
143 bool gui_hidden = false;
144 const char * gui_hide;
145 const char * gui_tip;
147 name = in_repr->attribute("name");
148 guitext = in_repr->attribute("gui-text");
149 if (guitext == NULL)
150 guitext = in_repr->attribute("_gui-text");
151 gui_tip = in_repr->attribute("gui-tip");
152 if (gui_tip == NULL)
153 gui_tip = in_repr->attribute("_gui-tip");
154 desc = in_repr->attribute("gui-description");
155 if (desc == NULL)
156 desc = in_repr->attribute("_gui-description");
157 scope_str = in_repr->attribute("scope");
158 gui_hide = in_repr->attribute("gui-hidden");
159 if (gui_hide != NULL) {
160 if (strcmp(gui_hide, "1") == 0 ||
161 strcmp(gui_hide, "true") == 0) {
162 gui_hidden = true;
163 }
164 /* else stays false */
165 }
167 /* In this case we just don't have enough information */
168 if (name == NULL) {
169 return NULL;
170 }
172 if (scope_str != NULL) {
173 if (!strcmp(scope_str, "user")) {
174 scope = Parameter::SCOPE_USER;
175 } else if (!strcmp(scope_str, "document")) {
176 scope = Parameter::SCOPE_DOCUMENT;
177 } else if (!strcmp(scope_str, "node")) {
178 scope = Parameter::SCOPE_NODE;
179 }
180 }
182 ParamNotebookPage * page = new ParamNotebookPage(name, guitext, desc, scope, gui_hide, gui_tip, in_ext, in_repr);
184 /* Note: page could equal NULL */
185 return page;
186 }
190 /**
191 \brief Creates a notebookpage widget for a notebook
193 Builds a notebook page (a vbox) and puts parameters on it.
194 */
195 Gtk::Widget *
196 ParamNotebookPage::get_widget (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
197 {
198 if (_gui_hidden) return NULL;
200 if (!_tooltips) _tooltips = new Gtk::Tooltips();
202 Gtk::VBox * vbox = Gtk::manage(new Gtk::VBox);
203 vbox->set_border_width(5);
205 // add parameters onto page (if any)
206 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
207 Parameter * param = reinterpret_cast<Parameter *>(list->data);
208 Gtk::Widget * widg = param->get_widget(doc, node, changeSignal);
209 gchar const * tip = param->get_tooltip();
211 vbox->pack_start(*widg, true, true, 2);
212 if (tip != NULL) {
213 _tooltips->set_tip(*widg, Glib::ustring(tip));
214 }
215 }
217 vbox->show();
219 return dynamic_cast<Gtk::Widget *>(vbox);
220 }
230 ParamNotebook::ParamNotebook (const gchar * name, const gchar * guitext, const gchar * desc, const Parameter::_scope_t scope, bool gui_hidden, const gchar * gui_tip, Inkscape::Extension::Extension * ext, Inkscape::XML::Node * xml) :
231 Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext)
232 {
233 pages = NULL;
235 // Read XML tree to add pages:
236 if (xml != NULL) {
237 Inkscape::XML::Node *child_repr = sp_repr_children(xml);
238 while (child_repr != NULL) {
239 char const * chname = child_repr->name();
240 if (chname[0] == '_') // Allow _ for translation of tags
241 chname++;
242 if (!strcmp(chname, "page")) {
243 ParamNotebookPage * page;
244 page = ParamNotebookPage::makepage(child_repr, ext);
245 if (page != NULL) pages = g_slist_append(pages, page);
246 }
247 child_repr = sp_repr_next(child_repr);
248 }
249 }
251 // Initialize _value with the current page
252 const char * defaultval = NULL;
253 // set first page as default
254 if (pages != NULL) {
255 ParamNotebookPage * defpage = reinterpret_cast<ParamNotebookPage *>(pages->data);
256 defaultval = defpage->name();
257 }
259 gchar * pref_name = this->pref_name();
260 const gchar * paramval = prefs_get_string_attribute(PREF_DIR, pref_name);
261 g_free(pref_name);
263 if (paramval != NULL)
264 defaultval = paramval;
265 if (defaultval != NULL)
266 _value = g_strdup(defaultval); // allocate space for _value
268 return;
269 }
271 ParamNotebook::~ParamNotebook (void)
272 {
273 //destroy pages
274 for (GSList * list = pages; list != NULL; list = g_slist_next(list)) {
275 ParamNotebookPage * page = reinterpret_cast<ParamNotebookPage *>(list->data);
276 delete page;
277 }
278 g_slist_free(pages);
280 g_free(_value);
281 }
284 /** \brief A function to set the \c _value
285 \param in The number of the page which value must be set
286 \param doc A document that should be used to set the value.
287 \param node The node where the value may be placed
289 This function sets the internal value, but it also sets the value
290 in the preferences structure. To put it in the right place, \c PREF_DIR
291 and \c pref_name() are used.
293 To copy the data into _value the old memory must be free'd first.
294 It is important to note that \c g_free handles \c NULL just fine. Then
295 the passed in value is duplicated using \c g_strdup().
296 */
297 const gchar *
298 ParamNotebook::set (const int in, SPDocument * /*doc*/, Inkscape::XML::Node * /*node*/)
299 {
300 ParamNotebookPage * page = NULL;
301 int i = 0;
302 for (GSList * list = pages; (list != NULL) && (i <= in); list = g_slist_next(list)) {
303 page = reinterpret_cast<ParamNotebookPage *>(list->data);
304 i++;
305 }
307 if (page == NULL) return _value;
309 if (_value != NULL) g_free(_value);
310 _value = g_strdup(page->name());
312 gchar * prefname = this->pref_name();
313 prefs_set_string_attribute(PREF_DIR, prefname, _value);
314 g_free(prefname);
316 return _value;
317 }
320 /**
321 \brief A function to get the currentpage and the parameters in a string form
322 \return A string with the 'value' and all the parameters on all pages as command line arguments
323 */
324 void
325 ParamNotebook::string (std::list <std::string> &list)
326 {
327 std::string param_string;
328 param_string += "--";
329 param_string += name();
330 param_string += "=";
332 param_string += "\"";
333 param_string += _value; // the name of the current page
334 param_string += "\"";
335 list.insert(list.end(), param_string);
337 for (GSList * pglist = pages; pglist != NULL; pglist = g_slist_next(pglist)) {
338 ParamNotebookPage * page = reinterpret_cast<ParamNotebookPage *>(pglist->data);
339 page->paramString(list);
340 }
342 return;
343 }
345 /** \brief A special category of Gtk::Notebook to handle notebook parameters */
346 class ParamNotebookWdg : public Gtk::Notebook {
347 private:
348 ParamNotebook * _pref;
349 SPDocument * _doc;
350 Inkscape::XML::Node * _node;
351 public:
352 /** \brief Build a notebookpage preference for the given parameter
353 \param pref Where to get the string (pagename) from, and where to put it
354 when it changes.
355 */
356 ParamNotebookWdg (ParamNotebook * pref, SPDocument * doc, Inkscape::XML::Node * node) :
357 Gtk::Notebook(), _pref(pref), _doc(doc), _node(node), activated(false) {
358 // don't have to set the correct page: this is done in ParamNotebook::get_widget.
359 // hook function
360 this->signal_switch_page().connect(sigc::mem_fun(this, &ParamNotebookWdg::changed_page));
361 return;
362 };
363 void changed_page(GtkNotebookPage *page, guint pagenum);
364 bool activated;
365 };
367 /** \brief Respond to the selected page of notebook changing
368 This function responds to the changing by reporting it to
369 ParamNotebook. The change is only reported when the notebook
370 is actually visible. This to exclude 'fake' changes when the
371 notebookpages are added or removed.
372 */
373 void
374 ParamNotebookWdg::changed_page(GtkNotebookPage */*page*/,
375 guint pagenum)
376 {
377 if (is_visible()) {
378 _pref->set((int)pagenum, _doc, _node);
379 }
380 return;
381 }
385 /**
386 \brief Creates a Notebook widget for a notebook parameter
388 Builds a notebook and puts pages in it.
389 */
390 Gtk::Widget *
391 ParamNotebook::get_widget (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
392 {
393 if (_gui_hidden) return NULL;
395 ParamNotebookWdg * nb = Gtk::manage(new ParamNotebookWdg(this, doc, node));
397 // add pages (if any)
398 int i = -1;
399 int pagenr = i;
400 for (GSList * list = pages; list != NULL; list = g_slist_next(list)) {
401 i++;
402 ParamNotebookPage * page = reinterpret_cast<ParamNotebookPage *>(list->data);
403 Gtk::Widget * widg = page->get_widget(doc, node, changeSignal);
404 nb->append_page(*widg, _(page->get_guitext()));
405 if (!strcmp(_value, page->name())) {
406 pagenr = i; // this is the page to be displayed?
407 }
408 }
410 nb->show();
412 if (pagenr >= 0) nb->set_current_page(pagenr);
414 return dynamic_cast<Gtk::Widget *>(nb);
415 }
418 } /* namespace Extension */
419 } /* namespace Inkscape */
421 /*
422 Local Variables:
423 mode:c++
424 c-file-style:"stroustrup"
425 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
426 indent-tabs-mode:nil
427 fill-column:99
428 End:
429 */
430 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :