/* This file is part of Ingen. * Copyright (C) 2007-2009 David Robillard * * Ingen is free software; you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "module/World.hpp" #include "shared/LV2URIMap.hpp" #include "client/ClientStore.hpp" #include "ingen/EngineInterface.hpp" #include "serialisation/Serialiser.hpp" #include "serialisation/names.hpp" #include "client/PatchModel.hpp" #include "UploadPatchWindow.hpp" #include "App.hpp" #include "Configuration.hpp" #include "ThreadedLoader.hpp" using boost::optional; using namespace Raul; using namespace std; namespace Ingen { namespace GUI { UploadPatchWindow::UploadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr& xml) : Dialog(cobject) , _thread(NULL) , _progress_pct(0) , _response(0) { xml->get_widget("upload_patch_symbol_entry", _symbol_entry); xml->get_widget("upload_patch_short_name_entry", _short_name_entry); xml->get_widget("upload_patch_progress", _upload_progress); xml->get_widget("upload_patch_cancel_button", _cancel_button); xml->get_widget("upload_patch_upload_button", _upload_button); _symbol_entry->signal_changed().connect(sigc::mem_fun(this, &UploadPatchWindow::symbol_changed)); _short_name_entry->signal_changed().connect(sigc::mem_fun(this, &UploadPatchWindow::short_name_changed)); _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &UploadPatchWindow::cancel_clicked)); _upload_button->signal_clicked().connect(sigc::mem_fun(this, &UploadPatchWindow::upload_clicked)); } void UploadPatchWindow::present(SharedPtr patch) { _patch = patch; Gtk::Window::present(); } void UploadPatchWindow::on_show() { const Shared::LV2URIMap& uris = App::instance().uris(); Gtk::Dialog::on_show(); Raul::Atom atom = _patch->get_property(uris.lv2_symbol); if (atom.is_valid()) _symbol_entry->set_text(atom.get_string()); atom = _patch->get_property(uris.doap_name); if (atom.is_valid()) _short_name_entry->set_text(atom.get_string()); } void UploadPatchWindow::on_hide() { Gtk::Dialog::on_hide(); delete _thread; _thread = NULL; } bool UploadPatchWindow::is_symbol(const Glib::ustring& s) { if (s.length() == 0) return false; for (unsigned i=0; i < s.length(); ++i) if ( !( (s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z') || (s[i] == '_') || (i > 0 && s[i] >= '0' && s[i] <= '9') ) ) return false; return true; } void UploadPatchWindow::symbol_changed() { _upload_button->property_sensitive() = ( is_symbol(_symbol_entry->get_text()) && _short_name_entry->get_text().length() > 0); } void UploadPatchWindow::short_name_changed() { _upload_button->property_sensitive() = ( is_symbol(_symbol_entry->get_text()) && _short_name_entry->get_text().length() > 0); } size_t UploadThread::curl_read_cb(void *ptr, size_t size, size_t nmemb, void *data) { assert(size == 1); istringstream* ss = (istringstream*)data; return ss->readsome((char*)ptr, nmemb); } int UploadThread::curl_progress_cb(void *thread, double dltotal, double dlnow, double ultotal, double ulnow) { UploadThread* me = (UploadThread*)thread; me->_win->set_progress(min( (int)(min(ulnow, (double)me->_length) / me->_length * 100.0), 99)); return 0; } UploadThread::UploadThread(UploadPatchWindow* win, const string& str, const string& url) : Thread("Upload") , _curl(NULL) , _headers(NULL) , _win(win) , _length(str.length()) , _stream(str) , _url(url) { _curl = curl_easy_init(); _headers = curl_slist_append(NULL, "Content-type: application/x-turtle"); curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _headers); curl_easy_setopt(_curl, CURLOPT_UPLOAD, 1); curl_easy_setopt(_curl, CURLOPT_READDATA, &_stream); curl_easy_setopt(_curl, CURLOPT_READFUNCTION, &UploadThread::curl_read_cb); curl_easy_setopt(_curl, CURLOPT_INFILESIZE, sizeof(char) * str.length()); curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, FALSE); curl_easy_setopt(_curl, CURLOPT_PROGRESSFUNCTION, &UploadThread::curl_progress_cb); curl_easy_setopt(_curl, CURLOPT_PROGRESSDATA, this); } void UploadThread::_run() { curl_easy_perform(_curl); long response; curl_easy_getinfo(_curl, CURLINFO_RESPONSE_CODE, &response); printf("Server returned %ld\n", response); _win->set_response(response); _win->set_progress(100); curl_slist_free_all(_headers); curl_easy_cleanup(_curl); _headers = NULL; _curl = NULL; } bool UploadPatchWindow::progress_callback() { const int progress = _progress_pct.get(); const int response = _response.get(); _upload_progress->set_fraction(progress / 100.0); if (progress == 100) { if (response == 200) { _upload_progress->set_text("Transfer completed"); } else { _upload_progress->set_fraction(0.0); char status[4]; snprintf(status, 4, "%d", (unsigned)response); string msg = "Transfer failed: Server returned "; msg.append(status); _upload_progress->set_text(msg); } delete _thread; _thread = NULL; _upload_button->set_sensitive(true); return false; } else { return true; } } void UploadPatchWindow::upload_clicked() { assert(!_thread); const Shared::LV2URIMap& uris = App::instance().uris(); Glib::ustring symbol = _symbol_entry->get_text(); Glib::ustring short_name = _short_name_entry->get_text(); GraphObject::Properties extra_rdf; extra_rdf.insert(make_pair(uris.lv2_symbol, symbol)); extra_rdf.insert(make_pair(uris.doap_name, short_name)); _response = 0; _progress_pct = 0; _upload_progress->set_fraction(0.0); _upload_progress->set_text(""); Serialiser s(*App::instance().world(), App::instance().store()); const string uri = string("http://rdf.drobilla.net/ingen_patches/") .append(symbol).append(INGEN_PATCH_FILE_EXT); const string str = s.to_string(_patch, uri, extra_rdf); _thread = new UploadThread(this, str, uri); _thread->start(); _upload_button->set_sensitive(false); Glib::signal_timeout().connect( sigc::mem_fun(this, &UploadPatchWindow::progress_callback), 100); } void UploadPatchWindow::cancel_clicked() { if (_thread) { delete _thread; _thread = NULL; } hide(); } } // namespace GUI } // namespace Ingen