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