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 "preferences.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 (!strncmp(chname, INKSCAPE_EXTENSION_NS_NC, strlen(INKSCAPE_EXTENSION_NS_NC))) {
75 chname += strlen(INKSCAPE_EXTENSION_NS);
76 }
77 if (chname[0] == '_') // Allow _ for translation of tags
78 chname++;
79 if (!strcmp(chname, "param") || !strcmp(chname, "_param")) {
80 Parameter * param;
81 param = Parameter::make(child_repr, ext);
82 if (param != NULL) parameters = g_slist_append(parameters, param);
83 }
84 child_repr = sp_repr_next(child_repr);
85 }
86 }
88 return;
89 }
91 ParamNotebookPage::~ParamNotebookPage (void)
92 {
93 if (_tooltips) delete _tooltips;
94 //destroy parameters
95 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
96 Parameter * param = reinterpret_cast<Parameter *>(list->data);
97 delete param;
98 }
99 g_slist_free(parameters);
100 }
102 /** \brief Return the value as a string */
103 void
104 ParamNotebookPage::paramString (std::list <std::string> &list)
105 {
106 for (GSList * plist = parameters; plist != NULL; plist = g_slist_next(plist)) {
107 Parameter * param = reinterpret_cast<Parameter *>(plist->data);
108 param->string(list);
109 }
111 return;
112 }
115 /**
116 \return None
117 \brief This function creates a page that can be used later. This
118 is typically done in the creation of the notebook and defined
119 in the XML file describing the extension (it's private so people
120 have to use the system) :)
121 \param in_repr The XML describing the page
123 This function first grabs all of the data out of the Repr and puts
124 it into local variables. Actually, these are just pointers, and the
125 data is not duplicated so we need to be careful with it. If there
126 isn't a name in the XML, then no page is created as
127 the function just returns.
129 From this point on, we're pretty committed as we've allocated an
130 object and we're starting to fill it. The name is set first, and
131 is created with a strdup to actually allocate memory for it. Then
132 there is a case statement (roughly because strcmp requires 'ifs')
133 based on what type of parameter this is. Depending which type it
134 is, the value is interpreted differently, but they are relatively
135 straight forward. In all cases the value is set to the default
136 value from the XML and the type is set to the interpreted type.
137 */
138 ParamNotebookPage *
139 ParamNotebookPage::makepage (Inkscape::XML::Node * in_repr, Inkscape::Extension::Extension * in_ext)
140 {
141 const char * name;
142 const char * guitext;
143 const char * desc;
144 const char * scope_str;
145 Parameter::_scope_t scope = Parameter::SCOPE_USER;
146 bool gui_hidden = false;
147 const char * gui_hide;
148 const char * gui_tip;
150 name = in_repr->attribute("name");
151 guitext = in_repr->attribute("gui-text");
152 if (guitext == NULL)
153 guitext = in_repr->attribute("_gui-text");
154 gui_tip = in_repr->attribute("gui-tip");
155 if (gui_tip == NULL)
156 gui_tip = in_repr->attribute("_gui-tip");
157 desc = in_repr->attribute("gui-description");
158 if (desc == NULL)
159 desc = in_repr->attribute("_gui-description");
160 scope_str = in_repr->attribute("scope");
161 gui_hide = in_repr->attribute("gui-hidden");
162 if (gui_hide != NULL) {
163 if (strcmp(gui_hide, "1") == 0 ||
164 strcmp(gui_hide, "true") == 0) {
165 gui_hidden = true;
166 }
167 /* else stays false */
168 }
170 /* In this case we just don't have enough information */
171 if (name == NULL) {
172 return NULL;
173 }
175 if (scope_str != NULL) {
176 if (!strcmp(scope_str, "user")) {
177 scope = Parameter::SCOPE_USER;
178 } else if (!strcmp(scope_str, "document")) {
179 scope = Parameter::SCOPE_DOCUMENT;
180 } else if (!strcmp(scope_str, "node")) {
181 scope = Parameter::SCOPE_NODE;
182 }
183 }
185 ParamNotebookPage * page = new ParamNotebookPage(name, guitext, desc, scope, gui_hide, gui_tip, in_ext, in_repr);
187 /* Note: page could equal NULL */
188 return page;
189 }
193 /**
194 \brief Creates a notebookpage widget for a notebook
196 Builds a notebook page (a vbox) and puts parameters on it.
197 */
198 Gtk::Widget *
199 ParamNotebookPage::get_widget (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
200 {
201 if (_gui_hidden) return NULL;
203 if (!_tooltips) _tooltips = new Gtk::Tooltips();
205 Gtk::VBox * vbox = Gtk::manage(new Gtk::VBox);
206 vbox->set_border_width(5);
208 // add parameters onto page (if any)
209 for (GSList * list = parameters; list != NULL; list = g_slist_next(list)) {
210 Parameter * param = reinterpret_cast<Parameter *>(list->data);
211 Gtk::Widget * widg = param->get_widget(doc, node, changeSignal);
212 gchar const * tip = param->get_tooltip();
214 vbox->pack_start(*widg, true, true, 2);
215 if (tip != NULL) {
216 _tooltips->set_tip(*widg, Glib::ustring(tip));
217 }
218 }
220 vbox->show();
222 return dynamic_cast<Gtk::Widget *>(vbox);
223 }
226 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) :
227 Parameter(name, guitext, desc, scope, gui_hidden, gui_tip, ext)
228 {
229 pages = NULL;
231 // Read XML tree to add pages:
232 if (xml != NULL) {
233 Inkscape::XML::Node *child_repr = sp_repr_children(xml);
234 while (child_repr != NULL) {
235 char const * chname = child_repr->name();
236 if (!strncmp(chname, INKSCAPE_EXTENSION_NS_NC, strlen(INKSCAPE_EXTENSION_NS_NC))) {
237 chname += strlen(INKSCAPE_EXTENSION_NS);
238 }
239 if (chname[0] == '_') // Allow _ for translation of tags
240 chname++;
241 if (!strcmp(chname, "page")) {
242 ParamNotebookPage * page;
243 page = ParamNotebookPage::makepage(child_repr, ext);
244 if (page != NULL) pages = g_slist_append(pages, page);
245 }
246 child_repr = sp_repr_next(child_repr);
247 }
248 }
250 // Initialize _value with the current page
251 const char * defaultval = NULL;
252 // set first page as default
253 if (pages != NULL) {
254 ParamNotebookPage * defpage = reinterpret_cast<ParamNotebookPage *>(pages->data);
255 defaultval = defpage->name();
256 }
258 gchar * pref_name = this->pref_name();
259 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
260 Glib::ustring paramval = prefs->getString(extension_pref_root + pref_name);
261 g_free(pref_name);
263 if (!paramval.empty())
264 defaultval = paramval.data();
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 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
314 prefs->setString(extension_pref_root + prefname, _value);
315 g_free(prefname);
317 return _value;
318 }
321 /**
322 \brief A function to get the currentpage and the parameters in a string form
323 \return A string with the 'value' and all the parameters on all pages as command line arguments
324 */
325 void
326 ParamNotebook::string (std::list <std::string> &list)
327 {
328 std::string param_string;
329 param_string += "--";
330 param_string += name();
331 param_string += "=";
333 param_string += "\"";
334 param_string += _value; // the name of the current page
335 param_string += "\"";
336 list.insert(list.end(), param_string);
338 for (GSList * pglist = pages; pglist != NULL; pglist = g_slist_next(pglist)) {
339 ParamNotebookPage * page = reinterpret_cast<ParamNotebookPage *>(pglist->data);
340 page->paramString(list);
341 }
343 return;
344 }
346 /** \brief A special category of Gtk::Notebook to handle notebook parameters */
347 class ParamNotebookWdg : public Gtk::Notebook {
348 private:
349 ParamNotebook * _pref;
350 SPDocument * _doc;
351 Inkscape::XML::Node * _node;
352 public:
353 /** \brief Build a notebookpage preference for the given parameter
354 \param pref Where to get the string (pagename) from, and where to put it
355 when it changes.
356 */
357 ParamNotebookWdg (ParamNotebook * pref, SPDocument * doc, Inkscape::XML::Node * node) :
358 Gtk::Notebook(), _pref(pref), _doc(doc), _node(node), activated(false) {
359 // don't have to set the correct page: this is done in ParamNotebook::get_widget.
360 // hook function
361 this->signal_switch_page().connect(sigc::mem_fun(this, &ParamNotebookWdg::changed_page));
362 return;
363 };
364 void changed_page(GtkNotebookPage *page, guint pagenum);
365 bool activated;
366 };
368 /** \brief Respond to the selected page of notebook changing
369 This function responds to the changing by reporting it to
370 ParamNotebook. The change is only reported when the notebook
371 is actually visible. This to exclude 'fake' changes when the
372 notebookpages are added or removed.
373 */
374 void
375 ParamNotebookWdg::changed_page(GtkNotebookPage */*page*/,
376 guint pagenum)
377 {
378 if (is_visible()) {
379 _pref->set((int)pagenum, _doc, _node);
380 }
381 return;
382 }
386 /**
387 \brief Creates a Notebook widget for a notebook parameter
389 Builds a notebook and puts pages in it.
390 */
391 Gtk::Widget *
392 ParamNotebook::get_widget (SPDocument * doc, Inkscape::XML::Node * node, sigc::signal<void> * changeSignal)
393 {
394 if (_gui_hidden) return NULL;
396 ParamNotebookWdg * nb = Gtk::manage(new ParamNotebookWdg(this, doc, node));
398 // add pages (if any)
399 int i = -1;
400 int pagenr = i;
401 for (GSList * list = pages; list != NULL; list = g_slist_next(list)) {
402 i++;
403 ParamNotebookPage * page = reinterpret_cast<ParamNotebookPage *>(list->data);
404 Gtk::Widget * widg = page->get_widget(doc, node, changeSignal);
405 nb->append_page(*widg, _(page->get_guitext()));
406 if (!strcmp(_value, page->name())) {
407 pagenr = i; // this is the page to be displayed?
408 }
409 }
411 nb->show();
413 if (pagenr >= 0) nb->set_current_page(pagenr);
415 return dynamic_cast<Gtk::Widget *>(nb);
416 }
419 } /* namespace Extension */
420 } /* namespace Inkscape */
422 /*
423 Local Variables:
424 mode:c++
425 c-file-style:"stroustrup"
426 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
427 indent-tabs-mode:nil
428 fill-column:99
429 End:
430 */
431 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :