Code

From trunk
[inkscape.git] / src / ui / dialog / ocaldialogs.cpp
1 /** @file
2  * @brief Open Clip Art Library integration dialogs - implementation
3  */
4 /* Authors:
5  *   Bruno Dilly
6  *   Other dudes from The Inkscape Organization
7  *
8  * Copyright (C) 2007 Bruno Dilly <bruno.dilly@gmail.com>
9  *
10  * Released under GNU GPL, read the file 'COPYING' for more information
11  */
13 #ifdef HAVE_CONFIG_H
14 # include <config.h>
15 #endif
17 #include <stdio.h>  // rename()
18 #include <unistd.h> // close()
19 #include <errno.h>  // errno
20 #include <string.h> // strerror()
22 #include "ocaldialogs.h"
23 #include "filedialogimpl-gtkmm.h"
24 #include "interface.h"
25 #include "gc-core.h"
26 #include <dialogs/dialog-events.h>
27 #include "io/sys.h"
28 #include "preferences.h"
30 namespace Inkscape
31 {
32 namespace UI
33 {
34 namespace Dialog
35 {
37 //########################################################################
38 //# F I L E    E X P O R T   T O   O C A L
39 //########################################################################
41 /**
42  * Callback for fileNameEntry widget
43  */
44 void FileExportToOCALDialog::fileNameEntryChangedCallback()
45 {
46     if (!fileNameEntry)
47         return;
49     Glib::ustring fileName = fileNameEntry->get_text();
50     if (!Glib::get_charset()) //If we are not utf8
51         fileName = Glib::filename_to_utf8(fileName);
53     myFilename = fileName;
54     response(Gtk::RESPONSE_OK);
55 }
57 /**
58  * Constructor
59  */
60 FileExportToOCALDialog::FileExportToOCALDialog(Gtk::Window &parentWindow,
61             FileDialogType fileTypes,
62             const Glib::ustring &title) :
63     FileDialogOCALBase(title, parentWindow)
64 {
65     /*
66      * Start Taking the vertical Box and putting a Label
67      * and a Entry to take the filename
68      * Later put the extension selection and checkbox (?)
69      */
70     /* Initalize to Autodetect */
71     extension = NULL;
72     /* No filename to start out with */
73     myFilename = "";
74     /* Set our dialog type (save, export, etc...)*/
75     dialogType = fileTypes;
76     Gtk::VBox *vbox = get_vbox();
78     Gtk::Label *fileLabel = new Gtk::Label(_("File"));
80     fileNameEntry = new Gtk::Entry();
81     fileNameEntry->set_text(myFilename);
82     fileNameEntry->set_max_length(252); // I am giving the extension approach.
83     fileBox.pack_start(*fileLabel);
84     fileBox.pack_start(*fileNameEntry, Gtk::PACK_EXPAND_WIDGET, 3);
85     vbox->pack_start(fileBox);
87     //Let's do some customization
88     fileNameEntry = NULL;
89     Gtk::Container *cont = get_toplevel();
90     std::vector<Gtk::Entry *> entries;
91     findEntryWidgets(cont, entries);
92     if (entries.size() >=1 )
93         {
94         //Catch when user hits [return] on the text field
95         fileNameEntry = entries[0];
96         fileNameEntry->signal_activate().connect(
97              sigc::mem_fun(*this, &FileExportToOCALDialog::fileNameEntryChangedCallback) );
98         }
100     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
101     set_default(*add_button(Gtk::Stock::SAVE,   Gtk::RESPONSE_OK));
103     show_all_children();
106 /**
107  * Destructor
108  */
109 FileExportToOCALDialog::~FileExportToOCALDialog()
113 /**
114  * Show this dialog modally.  Return true if user hits [OK]
115  */
116 bool
117 FileExportToOCALDialog::show()
119     set_modal (TRUE);                      //Window
120     sp_transientize((GtkWidget *)gobj());  //Make transient
121     gint b = run();                        //Dialog
122     hide();
124     if (b == Gtk::RESPONSE_OK)
125     {
126         return TRUE;
127         }
128     else
129         {
130         return FALSE;
131         }
134 /**
135  * Get the file name chosen by the user.   Valid after an [OK]
136  */
137 Glib::ustring
138 FileExportToOCALDialog::getFilename()
140     myFilename = fileNameEntry->get_text();
141     if (!Glib::get_charset()) //If we are not utf8
142         myFilename = Glib::filename_to_utf8(myFilename);
144     return myFilename;
148 void
149 FileExportToOCALDialog::change_title(const Glib::ustring& title)
151     this->set_title(title);
155 //########################################################################
156 //# F I L E    E X P O R T   T O   O C A L   P A S S W O R D
157 //########################################################################
160 /**
161  * Constructor
162  */
163 FileExportToOCALPasswordDialog::FileExportToOCALPasswordDialog(Gtk::Window &parentWindow,
164                              const Glib::ustring &title) : FileDialogOCALBase(title, parentWindow)
166     /*
167      * Start Taking the vertical Box and putting 2 Labels
168      * and 2 Entries to take the username and password
169      */
170     /* No username and password to start out with */
171     myUsername = "";
172     myPassword = "";
174     Gtk::VBox *vbox = get_vbox();
176     Gtk::Label *userLabel = new Gtk::Label(_("Username:"));
177     Gtk::Label *passLabel = new Gtk::Label(_("Password:"));
179     usernameEntry = new Gtk::Entry();
180     usernameEntry->set_text(myUsername);
181     usernameEntry->set_max_length(255);
183     passwordEntry = new Gtk::Entry();
184     passwordEntry->set_text(myPassword);
185     passwordEntry->set_max_length(255);
186     passwordEntry->set_invisible_char('*');
187     passwordEntry->set_visibility(false);
188     passwordEntry->set_activates_default(true);
190     userBox.pack_start(*userLabel);
191     userBox.pack_start(*usernameEntry, Gtk::PACK_EXPAND_WIDGET, 3);
192     vbox->pack_start(userBox);
194     passBox.pack_start(*passLabel);
195     passBox.pack_start(*passwordEntry, Gtk::PACK_EXPAND_WIDGET, 3);
196     vbox->pack_start(passBox);
198     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
199     set_default(*add_button(Gtk::Stock::OK,   Gtk::RESPONSE_OK));
201     show_all_children();
205 /**
206  * Destructor
207  */
208 FileExportToOCALPasswordDialog::~FileExportToOCALPasswordDialog()
212 /**
213  * Show this dialog modally.  Return true if user hits [OK]
214  */
215 bool
216 FileExportToOCALPasswordDialog::show()
218     set_modal (TRUE);                      //Window
219     sp_transientize((GtkWidget *)gobj());  //Make transient
220     gint b = run();                        //Dialog
221     hide();
223     if (b == Gtk::RESPONSE_OK)
224     {
225         return TRUE;
226     }
227     else
228     {
229         return FALSE;
230     }
233 /**
234  * Get the username.   Valid after an [OK]
235  */
236 Glib::ustring
237 FileExportToOCALPasswordDialog::getUsername()
239     myUsername = usernameEntry->get_text();
240     return myUsername;
243 /**
244  * Get the password.   Valid after an [OK]
245  */
246 Glib::ustring
247 FileExportToOCALPasswordDialog::getPassword()
249     myPassword = passwordEntry->get_text();
250     return myPassword;
253 void
254 FileExportToOCALPasswordDialog::change_title(const Glib::ustring& title)
256     this->set_title(title);
260 //#########################################################################
261 //### F I L E   I M P O R T   F R O M   O C A L
262 //#########################################################################
264 /*
265  * Calalback for cursor chage
266  */
267 void FileListViewText::on_cursor_changed()
269     std::vector<Gtk::TreeModel::Path> pathlist;
270     pathlist = this->get_selection()->get_selected_rows();
271     std::vector<int> posArray(1);
272     posArray = pathlist[0].get_indices();
274 #ifdef WITH_GNOME_VFS
275     gnome_vfs_init();
276     GnomeVFSHandle    *from_handle = NULL;
277     GnomeVFSHandle    *to_handle = NULL;
278     GnomeVFSFileSize  bytes_read;
279     GnomeVFSFileSize  bytes_written;
280     GnomeVFSResult    result;
281     guint8 buffer[8192];
282     Glib::ustring fileUrl;
284     // FIXME: this would be better as a per-user OCAL cache of files
285     // instead of filling /tmp with downloads.
286     //
287     // create file path
288     const std::string tmptemplate = "ocal-";
289     std::string tmpname;
290     int fd = Inkscape::IO::file_open_tmp(tmpname, tmptemplate);
291     if (fd<0) {
292         g_warning("Error creating temp file");
293         return;
294     }
295     close(fd);
296     // make sure we don't collide with other users on the same machine
297     myFilename = tmpname;
298     myFilename.append("-");
299     myFilename.append(get_text(posArray[0], 2));
300     // rename based on original image's name, retaining extension
301     if (rename(tmpname.c_str(),myFilename.c_str())<0) {
302         unlink(tmpname.c_str());
303         g_warning("Error creating destination file '%s': %s", myFilename.c_str(), strerror(errno));
304         goto failquit;
305     }
307     //get file url
308     fileUrl = get_text(posArray[0], 1); //http url
310     //Inkscape::Preferences *prefs = Inkscape::Preferences::get();
311     //Glib::ustring fileUrl = "dav://"; //dav url
312     //fileUrl.append(prefs->getString("/options/ocalurl/str"));
313     //fileUrl.append("/dav.php/");
314     //fileUrl.append(get_text(posArray[0], 3)); //author dir
315     //fileUrl.append("/");
316     //fileUrl.append(get_text(posArray[0], 2)); //filename
318     if (!Glib::get_charset()) //If we are not utf8
319         fileUrl = Glib::filename_to_utf8(fileUrl);
321     {
322         // open the temp file to receive
323         result = gnome_vfs_open (&to_handle, myFilename.c_str(), GNOME_VFS_OPEN_WRITE);
324         if (result == GNOME_VFS_ERROR_NOT_FOUND){
325             result = gnome_vfs_create (&to_handle, myFilename.c_str(), GNOME_VFS_OPEN_WRITE, FALSE, GNOME_VFS_PERM_USER_ALL);
326         }
327         if (result != GNOME_VFS_OK) {
328             g_warning("Error creating temp file '%s': %s", myFilename.c_str(), gnome_vfs_result_to_string(result));
329             goto fail;
330         }
331         result = gnome_vfs_open (&from_handle, fileUrl.c_str(), GNOME_VFS_OPEN_READ);
332         if (result != GNOME_VFS_OK) {
333             g_warning("Could not find the file in Open Clip Art Library.");
334             goto fail;
335         }
336         // copy the file
337         while (1) {
338             result = gnome_vfs_read (from_handle, buffer, 8192, &bytes_read);
339             if ((result == GNOME_VFS_ERROR_EOF) &&(!bytes_read)){
340                 result = gnome_vfs_close (from_handle);
341                 result = gnome_vfs_close (to_handle);
342                 break;
343             }
344             if (result != GNOME_VFS_OK) {
345                 g_warning("%s", gnome_vfs_result_to_string(result));
346                 goto fail;
347             }
348             result = gnome_vfs_write (to_handle, buffer, bytes_read, &bytes_written);
349             if (result != GNOME_VFS_OK) {
350                 g_warning("%s", gnome_vfs_result_to_string(result));
351                 goto fail;
352             }
353             if (bytes_read != bytes_written){
354                 g_warning("Bytes read not equal to bytes written");
355                 goto fail;
356             }
357         }
358     }
359     myPreview->showImage(myFilename);
360     myLabel->set_text(get_text(posArray[0], 4));
361 #endif
362     return;
363 fail:
364     unlink(myFilename.c_str());
365 failquit:
366     myFilename = "";
370 /*
371  * Callback for row activated
372  */
373 void FileListViewText::on_row_activated(const Gtk::TreeModel::Path& /*path*/, Gtk::TreeViewColumn* /*column*/)
375     this->on_cursor_changed();
376     myButton->activate();
380 /*
381  * Returns the selected filename
382  */
383 Glib::ustring FileListViewText::getFilename()
385     return myFilename;
389 #ifdef WITH_GNOME_VFS
390 /**
391  * Read callback for xmlReadIO(), used below
392  */
393 static int vfs_read_callback (GnomeVFSHandle *handle, char* buf, int nb)
395     GnomeVFSFileSize ndone;
396     GnomeVFSResult    result;
398     result = gnome_vfs_read (handle, buf, nb, &ndone);
400     if (result == GNOME_VFS_OK) {
401         return (int)ndone;
402     } else {
403         if (result != GNOME_VFS_ERROR_EOF) {
404             sp_ui_error_dialog(_("Error while reading the Open Clip Art RSS feed"));
405             g_warning("%s\n", gnome_vfs_result_to_string(result));
406         }
407         return -1;
408     }
410 #endif
413 /**
414  * Callback for user input into searchTagEntry
415  */
416 void FileImportFromOCALDialog::searchTagEntryChangedCallback()
418     if (!searchTagEntry)
419         return;
421     notFoundLabel->hide();
422     descriptionLabel->set_text("");
423     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
425     Glib::ustring searchTag = searchTagEntry->get_text();
426     // create the ocal uri to get rss feed
427     Glib::ustring uri = "http://";
428     uri.append(prefs->getString("/options/ocalurl/str"));
429     uri.append("/media/feed/rss/");
430     uri.append(searchTag);
431     if (!Glib::get_charset()) //If we are not utf8
432         uri = Glib::filename_to_utf8(uri);
434 #ifdef WITH_GNOME_VFS
436     // open the rss feed
437     gnome_vfs_init();
438     GnomeVFSHandle    *from_handle = NULL;
439     GnomeVFSResult    result;
441     result = gnome_vfs_open (&from_handle, uri.c_str(), GNOME_VFS_OPEN_READ);
442     if (result != GNOME_VFS_OK) {
443         sp_ui_error_dialog(_("Failed to receive the Open Clip Art Library RSS feed. Verify if the server name is correct in Configuration->Import/Export (e.g.: openclipart.org)"));
444         return;
445     }
447     // create the resulting xml document tree
448     // this initialize the library and test mistakes between compiled and shared library used
449     LIBXML_TEST_VERSION
450     xmlDoc *doc = NULL;
451     xmlNode *root_element = NULL;
453     doc = xmlReadIO ((xmlInputReadCallback) vfs_read_callback,
454         (xmlInputCloseCallback) gnome_vfs_close, from_handle, uri.c_str(), NULL,
455         XML_PARSE_RECOVER);
456     if (doc == NULL) {
457         sp_ui_error_dialog(_("Server supplied malformed Clip Art feed"));
458         g_warning("Failed to parse %s\n", uri.c_str());
459         return;
460     }
462     // get the root element node
463     root_element = xmlDocGetRootElement(doc);
465     // clear the fileslist
466     filesList->clear_items();
467     filesList->set_sensitive(false);
469     // print all xml the element names
470     print_xml_element_names(root_element);
472     if (filesList->size() == 0)
473     {
474         notFoundLabel->show();
475         filesList->set_sensitive(false);
476     }
477     else
478         filesList->set_sensitive(true);
480     // free the document
481     xmlFreeDoc(doc);
482     // free the global variables that may have been allocated by the parser
483     xmlCleanupParser();
484     return;
485 #endif
489 /**
490  * Prints the names of the all the xml elements
491  * that are siblings or children of a given xml node
492  */
493 void FileImportFromOCALDialog::print_xml_element_names(xmlNode * a_node)
495     xmlNode *cur_node = NULL;
496     guint row_num = 0;
497     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
498         // get itens information
499         if (strcmp((const char*)cur_node->name, "rss")) //avoid the root
500             if (cur_node->type == XML_ELEMENT_NODE && !strcmp((const char*)cur_node->parent->name, "item"))
501             {
502                 if (!strcmp((const char*)cur_node->name, "title"))
503                 {
504                     xmlChar *title = xmlNodeGetContent(cur_node);
505                     row_num = filesList->append_text((const char*)title);
506                     xmlFree(title);
507                 }
508 #ifdef WITH_GNOME_VFS
509                 else if (!strcmp((const char*)cur_node->name, "enclosure"))
510                 {
511                     xmlChar *urlattribute = xmlGetProp(cur_node, (xmlChar*)"url");
512                     filesList->set_text(row_num, 1, (const char*)urlattribute);
513                     gchar *tmp_file;
514                     tmp_file = gnome_vfs_uri_extract_short_path_name(gnome_vfs_uri_new((const char*)urlattribute));
515                     filesList->set_text(row_num, 2, (const char*)tmp_file);
516                     xmlFree(urlattribute);
517                 }
518                 else if (!strcmp((const char*)cur_node->name, "creator"))
519                 {
520                     filesList->set_text(row_num, 3, (const char*)xmlNodeGetContent(cur_node));
521                 }
522                 else if (!strcmp((const char*)cur_node->name, "description"))
523                 {
524                     filesList->set_text(row_num, 4, (const char*)xmlNodeGetContent(cur_node));
525                 }
526 #endif
527             }
528         print_xml_element_names(cur_node->children);
529     }
532 /**
533  * Constructor.  Not called directly.  Use the factory.
534  */
535 FileImportFromOCALDialog::FileImportFromOCALDialog(Gtk::Window& parentWindow,
536                                                    const Glib::ustring &/*dir*/,
537                                                    FileDialogType fileTypes,
538                                                    const Glib::ustring &title) :
539     FileDialogOCALBase(title, parentWindow)
541     // Initalize to Autodetect
542     extension = NULL;
543     // No filename to start out with
544     Glib::ustring searchTag = "";
546     dialogType = fileTypes;
547     Gtk::VBox *vbox = get_vbox();
548     Gtk::Label *tagLabel = new Gtk::Label(_("Search for:"));
549     notFoundLabel = new Gtk::Label(_("No files matched your search"));
550     descriptionLabel = new Gtk::Label();
551     descriptionLabel->set_max_width_chars(260);
552     descriptionLabel->set_size_request(500, -1);
553     descriptionLabel->set_single_line_mode(false);
554     descriptionLabel->set_line_wrap(true);
555     messageBox.pack_start(*notFoundLabel);
556     descriptionBox.pack_start(*descriptionLabel);
557     searchTagEntry = new Gtk::Entry();
558     searchTagEntry->set_text(searchTag);
559     searchTagEntry->set_max_length(255);
560     searchButton = new Gtk::Button(_("Search"));
561     tagBox.pack_start(*tagLabel);
562     tagBox.pack_start(*searchTagEntry, Gtk::PACK_EXPAND_WIDGET, 3);
563     tagBox.pack_start(*searchButton);
564     filesPreview = new SVGPreview();
565     filesPreview->showNoPreview();
566     // add the buttons in the bottom of the dialog
567     add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
568     okButton = add_button(Gtk::Stock::OPEN,   Gtk::RESPONSE_OK);
569     // sets the okbutton to default
570     set_default(*okButton);
571     filesList = new FileListViewText(5, *filesPreview, *descriptionLabel, *okButton);
572     filesList->set_sensitive(false);
573     // add the listview inside a ScrolledWindow
574     listScrolledWindow.add(*filesList);
575     // only show the scrollbars when they are necessary:
576     listScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
577     filesList->set_column_title(0, _("Files found"));
578     listScrolledWindow.set_size_request(400, 180);
579     filesList->get_column(1)->set_visible(false); // file url
580     filesList->get_column(2)->set_visible(false); // tmp file path
581     filesList->get_column(3)->set_visible(false); // author dir
582     filesList->get_column(4)->set_visible(false); // file description
583     filesBox.pack_start(listScrolledWindow);
584     filesBox.pack_start(*filesPreview);
585     vbox->pack_start(tagBox, false, false);
586     vbox->pack_start(messageBox);
587     vbox->pack_start(filesBox);
588     vbox->pack_start(descriptionBox);
590     //Let's do some customization
591     searchTagEntry = NULL;
592     Gtk::Container *cont = get_toplevel();
593     std::vector<Gtk::Entry *> entries;
594     findEntryWidgets(cont, entries);
595     if (entries.size() >=1 )
596     {
597     //Catch when user hits [return] on the text field
598         searchTagEntry = entries[0];
599         searchTagEntry->signal_activate().connect(
600               sigc::mem_fun(*this, &FileImportFromOCALDialog::searchTagEntryChangedCallback));
601     }
603     searchButton->signal_clicked().connect(
604             sigc::mem_fun(*this, &FileImportFromOCALDialog::searchTagEntryChangedCallback));
606     show_all_children();
607     notFoundLabel->hide();
610 /**
611  * Destructor
612  */
613 FileImportFromOCALDialog::~FileImportFromOCALDialog()
618 /**
619  * Show this dialog modally.  Return true if user hits [OK]
620  */
621 bool
622 FileImportFromOCALDialog::show()
624     set_modal (TRUE);                      //Window
625     sp_transientize((GtkWidget *)gobj());  //Make transient
626     gint b = run();                        //Dialog
627     hide();
629     if (b == Gtk::RESPONSE_OK)
630     {
631         return TRUE;
632     }
633     else
634     {
635         return FALSE;
636     }
640 /**
641  * Get the file extension type that was selected by the user. Valid after an [OK]
642  */
643 Inkscape::Extension::Extension *
644 FileImportFromOCALDialog::getSelectionType()
646     return extension;
650 /**
651  * Get the file name chosen by the user.   Valid after an [OK]
652  */
653 Glib::ustring
654 FileImportFromOCALDialog::getFilename (void)
656     return filesList->getFilename();
660 } //namespace Dialog
661 } //namespace UI
662 } //namespace Inkscape
666 /*
667   Local Variables:
668   mode:c++
669   c-file-style:"stroustrup"
670   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
671   indent-tabs-mode:nil
672   fill-column:99
673   End:
674 */
675 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :