From 59d57a03d9673e4f8f9c07aa7ba84edef32b8976 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 15 Nov 2006 12:56:19 +0000 Subject: First implementation of ClipboardService. Implement pdf viewing and downloading via ClipboardService. --- diff --git a/.gitignore b/.gitignore index 0748927..edc5592 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,4 @@ lib/src/sugar-marshal.c lib/src/sugar-marshal.h lib/src/stamp-sugar-marshal.c lib/src/stamp-sugar-marshal.h +services/clipboard/org.laptop.Clipboard.service diff --git a/activities/web/webactivity.py b/activities/web/webactivity.py index 200bd39..14a763d 100644 --- a/activities/web/webactivity.py +++ b/activities/web/webactivity.py @@ -18,10 +18,12 @@ from gettext import gettext as _ import gtk import gtkmozembed import logging +import dbus import _sugar from sugar.activity import ActivityFactory from sugar.activity.Activity import Activity +from sugar.clipboard import ClipboardService from sugar import env from sugar.graphics import style import web.stylesheet @@ -104,12 +106,33 @@ def start(): style.load_stylesheet(web.stylesheet) - chandler = _sugar.get_browser_chandler() - chandler.connect('handle-content', handle_content_cb) + download_manager = _sugar.get_download_manager() + download_manager.connect('download-started', download_started_cb) + download_manager.connect('download-completed', download_completed_cb) + download_manager.connect('download-cancelled', download_started_cb) + download_manager.connect('download-progress', download_progress_cb) def stop(): gtkmozembed.pop_startup() -def handle_content_cb(chandler, url, mimeType, tmpFileName): - activity = ActivityFactory.create("org.laptop.sugar.Xbook") - activity.execute("open_document", [tmpFileName]) +def download_started_cb(download_manager, download): + name = download.get_url().rsplit('/', 1)[1] + + cbService = ClipboardService.get_instance() + cbService.add_object(name, + download.get_mime_type(), + download.get_file_name()) + +def download_completed_cb(download_manager, download): + cbService = ClipboardService.get_instance() + cbService.set_object_state(download.get_file_name(), 100) + +def download_cancelled_cb(download_manager, download): + #FIXME: Needs to update the state of the object to 'download stopped'. + #FIXME: Will do it when we complete progress on the definition of the + #FIXME: clipboard API. + raise "Cancelling downloads still not implemented." + +def download_progress_cb(download_manager, download): + cbService = ClipboardService.get_instance() + cbService.set_object_state(download.get_file_name(), download.get_percent()) diff --git a/configure.ac b/configure.ac index fd1539a..e96a4ff 100644 --- a/configure.ac +++ b/configure.ac @@ -64,6 +64,7 @@ lib/threadframe/Makefile services/Makefile services/presence/Makefile services/nm/Makefile +services/clipboard/Makefile shell/Makefile shell/conf/Makefile shell/data/Makefile @@ -76,6 +77,7 @@ sugar/__installed__.py sugar/activity/Makefile sugar/chat/Makefile sugar/chat/sketchpad/Makefile +sugar/clipboard/Makefile sugar/graphics/Makefile sugar/p2p/Makefile sugar/presence/Makefile diff --git a/lib/python/_sugar.defs b/lib/python/_sugar.defs index 5a31c91..d692be3 100644 --- a/lib/python/_sugar.defs +++ b/lib/python/_sugar.defs @@ -35,11 +35,18 @@ (gtype-id "SUGAR_TYPE_PUSH_SCROLLER") ) -(define-object BrowserChandler +(define-object DownloadManager (in-module "Sugar") (parent "GObject") - (c-name "SugarBrowserChandler") - (gtype-id "SUGAR_TYPE_BROWSER_CHANDLER") + (c-name "SugarDownloadManager") + (gtype-id "SUGAR_TYPE_DOWNLOAD_MANAGER") +) + +(define-object Download + (in-module "Sugar") + (parent "GObject") + (c-name "SugarDownload") + (gtype-id "SUGAR_TYPE_DOWNLOAD") ) ;; Enumerations and flags ... @@ -181,14 +188,45 @@ ) ) -;; From sugar-browser-chandler.h +;; From sugar-download-manager.h + +(define-function sugar_download_manager_get_type + (c-name "sugar_download_manager_get_type") + (return-type "GType") +) + +(define-function get_download_manager + (c-name "sugar_get_download_manager") + (return-type "SugarDownloadManager*") +) + +;; From sugar-download.h -(define-function sugar_browser_chandler_get_type - (c-name "sugar_browser_chandler_get_type") +(define-function sugar_download_get_type + (c-name "sugar_download_get_type") (return-type "GType") ) -(define-function get_browser_chandler - (c-name "sugar_get_browser_chandler") - (return-type "SugarBrowserChandler*") +(define-method get_file_name + (of-object "SugarDownload") + (c-name "sugar_download_get_file_name") + (return-type "const-gchar*") +) + +(define-method get_url + (of-object "SugarDownload") + (c-name "sugar_download_get_url") + (return-type "const-gchar*") +) + +(define-method get_mime_type + (of-object "SugarDownload") + (c-name "sugar_download_get_mime_type") + (return-type "const-gchar*") +) + +(define-method get_percent + (of-object "SugarDownload") + (c-name "sugar_download_get_percent") + (return-type "gint") ) diff --git a/lib/python/_sugar.override b/lib/python/_sugar.override index 9d27bbe..212f264 100644 --- a/lib/python/_sugar.override +++ b/lib/python/_sugar.override @@ -9,7 +9,8 @@ headers #include "sugar-address-entry.h" #include "sugar-tray-manager.h" #include "sugar-push-scroller.h" -#include "sugar-browser-chandler.h" +#include "sugar-download-manager.h" +#include "sugar-download.h" %% modulename gecko diff --git a/lib/src/GeckoContentHandler.cpp b/lib/src/GeckoContentHandler.cpp new file mode 100644 index 0000000..4d9de8e --- /dev/null +++ b/lib/src/GeckoContentHandler.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include "GeckoContentHandler.h" + +GeckoContentHandler::GeckoContentHandler() +{ + +} + +GeckoContentHandler::~GeckoContentHandler() +{ + +} + +NS_IMPL_ISUPPORTS1(GeckoContentHandler, nsIHelperAppLauncherDialog) + +NS_IMETHODIMP +GeckoContentHandler::Show (nsIHelperAppLauncher *aLauncher, + nsISupports *aContext, + PRUint32 aReason) +{ + nsCOMPtr tmpFile; + aLauncher->GetTargetFile(getter_AddRefs(tmpFile)); + + aLauncher->SaveToDisk (tmpFile, PR_FALSE); + + return NS_OK; +} + +NS_IMETHODIMP +GeckoContentHandler::PromptForSaveToFile (nsIHelperAppLauncher *aLauncher, + nsISupports *aWindowContext, + const PRUnichar *aDefaultFile, + const PRUnichar *aSuggestedFileExtension, + nsILocalFile **_retval) +{ + return NS_OK; +} + diff --git a/lib/src/SugarContentHandler.h b/lib/src/GeckoContentHandler.h index da5ae40..ef6f3e2 100644 --- a/lib/src/SugarContentHandler.h +++ b/lib/src/GeckoContentHandler.h @@ -1,10 +1,10 @@ -#ifndef __SUGAR_CONTENT_HANDLER_H__ -#define __SUGAR_CONTENT_HANDLER_H__ +#ifndef __GECKO_CONTENT_HANDLER_H__ +#define __GECKO_CONTENT_HANDLER_H__ #include #include -#define G_SUGARCONTENTHANDLER_CID \ +#define GECKOCONTENTHANDLER_CID \ { /* 2321843e-6377-11db-967b-00e08161165f */ \ 0x2321843e, \ 0x6377, \ @@ -12,17 +12,17 @@ {0x96, 0x7b, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f} \ } -class GSugarContentHandler : public nsIHelperAppLauncherDialog +class GeckoContentHandler : public nsIHelperAppLauncherDialog { public: NS_DECL_ISUPPORTS NS_DECL_NSIHELPERAPPLAUNCHERDIALOG - GSugarContentHandler(); - virtual ~GSugarContentHandler(); + GeckoContentHandler(); + virtual ~GeckoContentHandler(); private: }; -#endif /* __SUGAR_CONTENT_HANDLER_H */ +#endif /* __GECKO_CONTENT_HANDLER_H */ diff --git a/lib/src/GeckoDownload.cpp b/lib/src/GeckoDownload.cpp new file mode 100644 index 0000000..66eb7c8 --- /dev/null +++ b/lib/src/GeckoDownload.cpp @@ -0,0 +1,129 @@ +#include "sugar-download-manager.h" + +#include "GeckoDownload.h" + +GeckoDownload::GeckoDownload () +{ +} + +GeckoDownload::~GeckoDownload () +{ +} + +NS_IMPL_ISUPPORTS3 (GeckoDownload, + nsIWebProgressListener, + nsIWebProgressListener2, + nsITransfer) + +NS_IMETHODIMP +GeckoDownload::Init (nsIURI *aSource, + nsIURI *aTarget, + const nsAString &aDisplayName, + nsIMIMEInfo *aMIMEInfo, + PRTime aStartTime, + nsILocalFile *aTempFile, + nsICancelable *aCancelable) +{ + mSource = aSource; + aTarget->GetPath (mTargetFileName); + mMIMEInfo = aMIMEInfo; + mTempFile = aTempFile; +// mCancelable = aCancelable; Just a reminder for when we implement cancelling downloads. + + return NS_OK; +} + +NS_IMETHODIMP +GeckoDownload::OnStateChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRUint32 aStateFlags, + nsresult aStatus) +{ + SugarDownloadManager *download_manager = sugar_get_download_manager (); + + if (aStateFlags == STATE_START) { + + nsCString url; + nsCString mimeType; + + mMIMEInfo->GetMIMEType (mimeType); + mSource->GetSpec (url); + + sugar_download_manager_download_started (download_manager, + url.get (), + mimeType.get (), + mTargetFileName.get ()); + + } else if (aStateFlags == STATE_STOP) { + + if (NS_SUCCEEDED (aStatus)) { + sugar_download_manager_download_completed (download_manager, + mTargetFileName.get ()); + } else { + sugar_download_manager_download_cancelled (download_manager, + mTargetFileName.get ()); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +GeckoDownload::OnProgressChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRInt32 aCurSelfProgress, + PRInt32 aMaxSelfProgress, + PRInt32 aCurTotalProgress, + PRInt32 aMaxTotalProgress) +{ + return OnProgressChange64 (aWebProgress, + aRequest, + aCurSelfProgress, + aMaxSelfProgress, + aCurTotalProgress, + aMaxTotalProgress); +} + +NS_IMETHODIMP +GeckoDownload::OnProgressChange64 (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRInt64 aCurSelfProgress, + PRInt64 aMaxSelfProgress, + PRInt64 aCurTotalProgress, + PRInt64 aMaxTotalProgress) +{ + SugarDownloadManager *download_manager = sugar_get_download_manager (); + PRInt32 percentComplete = + (PRInt32)(((float)aCurSelfProgress / (float)aMaxSelfProgress) * 100.0); + + sugar_download_manager_update_progress (download_manager, + mTargetFileName.get (), + percentComplete); + + return NS_OK; +} + +NS_IMETHODIMP +GeckoDownload::OnLocationChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + nsIURI *location) +{ + return NS_OK; +} + +NS_IMETHODIMP +GeckoDownload::OnStatusChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + nsresult aStatus, + const PRUnichar *aMessage) +{ + return NS_OK; +} + +NS_IMETHODIMP +GeckoDownload::OnSecurityChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRUint32 state) +{ + return NS_OK; +} diff --git a/lib/src/SugarDownload.h b/lib/src/GeckoDownload.h index b858f53..24bff54 100644 --- a/lib/src/SugarDownload.h +++ b/lib/src/GeckoDownload.h @@ -1,5 +1,5 @@ -#ifndef SugarDownload_h__ -#define SugarDownload_h__ +#ifndef __GECKO_DOWNLOAD_H__ +#define __GECKO_DOWNLOAD_H__ #include #include @@ -10,7 +10,7 @@ #include #include -#define G_SUGARDOWNLOAD_CID \ +#define GECKODOWNLOAD_CID \ { /* b1813bbe-6518-11db-967e-00e08161165f */ \ 0xb1813bbe, \ 0x6518, \ @@ -18,11 +18,11 @@ {0x96, 0x7e, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f} \ } -class GSugarDownload : public nsITransfer +class GeckoDownload : public nsITransfer { public: - GSugarDownload(); - virtual ~GSugarDownload(); + GeckoDownload(); + virtual ~GeckoDownload(); NS_DECL_ISUPPORTS NS_DECL_NSIWEBPROGRESSLISTENER @@ -36,4 +36,4 @@ protected: nsILocalFile *mTempFile; }; -#endif // SugarDownload_h__ +#endif // __GECKO_DOWNLOAD_H__ diff --git a/lib/src/Makefile.am b/lib/src/Makefile.am index 0224885..3b51f1c 100644 --- a/lib/src/Makefile.am +++ b/lib/src/Makefile.am @@ -15,16 +15,18 @@ libsugarprivate_la_SOURCES = \ $(BUILT_SOURCES) \ eggaccelerators.h \ eggaccelerators.c \ + GeckoContentHandler.h \ + GeckoContentHandler.cpp \ + GeckoDownload.h \ + GeckoDownload.cpp \ sugar-address-entry.h \ sugar-address-entry.c \ sugar-browser.h \ sugar-browser.cpp \ - sugar-browser-chandler.h \ - sugar-browser-chandler.c \ - SugarContentHandler.h \ - SugarContentHandler.cpp \ - SugarDownload.h \ - SugarDownload.cpp \ + sugar-download.h \ + sugar-download.c \ + sugar-download-manager.h \ + sugar-download-manager.c \ sugar-key-grabber.h \ sugar-key-grabber.c \ sugar-push-scroller.c \ diff --git a/lib/src/SugarContentHandler.cpp b/lib/src/SugarContentHandler.cpp deleted file mode 100644 index b39b20b..0000000 --- a/lib/src/SugarContentHandler.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include - -#include "sugar-browser-chandler.h" -#include "SugarDownload.h" - -#include "SugarContentHandler.h" - -GSugarContentHandler::GSugarContentHandler() -{ - -} - -GSugarContentHandler::~GSugarContentHandler() -{ - -} - -NS_IMPL_ISUPPORTS1(GSugarContentHandler, nsIHelperAppLauncherDialog) - -NS_IMETHODIMP -GSugarContentHandler::Show (nsIHelperAppLauncher *aLauncher, - nsISupports *aContext, - PRUint32 aReason) -{ - nsCOMPtr tmpFile; - aLauncher->GetTargetFile(getter_AddRefs(tmpFile)); - - aLauncher->SaveToDisk (tmpFile, PR_FALSE); - - return NS_OK; -} - -NS_IMETHODIMP GSugarContentHandler::PromptForSaveToFile( - nsIHelperAppLauncher *aLauncher, - nsISupports *aWindowContext, - const PRUnichar *aDefaultFile, - const PRUnichar *aSuggestedFileExtension, - nsILocalFile **_retval) -{ - return NS_OK; -} - diff --git a/lib/src/SugarDownload.cpp b/lib/src/SugarDownload.cpp deleted file mode 100644 index 9c68b8f..0000000 --- a/lib/src/SugarDownload.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "sugar-browser-chandler.h" - -#include "SugarDownload.h" - -GSugarDownload::GSugarDownload() -{ -} - -GSugarDownload::~GSugarDownload() -{ -} - -NS_IMPL_ISUPPORTS3 (GSugarDownload, - nsIWebProgressListener, - nsIWebProgressListener2, - nsITransfer) - -NS_IMETHODIMP -GSugarDownload::Init (nsIURI *aSource, - nsIURI *aTarget, - const nsAString &aDisplayName, - nsIMIMEInfo *aMIMEInfo, - PRTime aStartTime, - nsILocalFile *aTempFile, - nsICancelable *aCancelable) -{ - mSource = aSource; - aTarget->GetPath(mTargetFileName); - mMIMEInfo = aMIMEInfo; - mTempFile = aTempFile; - - return NS_OK; -} - -NS_IMETHODIMP -GSugarDownload::OnStateChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, - PRUint32 aStateFlags, nsresult aStatus) -{ - nsCString url; - nsCString mimeType; - nsCString targetURI; - - if ((((aStateFlags & STATE_IS_REQUEST) && - (aStateFlags & STATE_IS_NETWORK) && - (aStateFlags & STATE_STOP)) || - aStateFlags == STATE_STOP) && - NS_SUCCEEDED (aStatus)) { - - mMIMEInfo->GetMIMEType(mimeType); - mSource->GetSpec(url); - - SugarBrowserChandler *browser_chandler = sugar_get_browser_chandler(); - sugar_browser_chandler_handle_content(browser_chandler, - url.get(), - mimeType.get(), - mTargetFileName.get()); - } - - return NS_OK; -} - -NS_IMETHODIMP -GSugarDownload::OnProgressChange (nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - PRInt32 aCurSelfProgress, - PRInt32 aMaxSelfProgress, - PRInt32 aCurTotalProgress, - PRInt32 aMaxTotalProgress) -{ - return OnProgressChange64 (aWebProgress, aRequest, - aCurSelfProgress, aMaxSelfProgress, - aCurTotalProgress, aMaxTotalProgress); -} - -NS_IMETHODIMP -GSugarDownload::OnProgressChange64 (nsIWebProgress *aWebProgress, - nsIRequest *aRequest, - PRInt64 aCurSelfProgress, - PRInt64 aMaxSelfProgress, - PRInt64 aCurTotalProgress, - PRInt64 aMaxTotalProgress) -{ - return NS_OK; -} - -NS_IMETHODIMP -GSugarDownload::OnLocationChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsIURI *location) -{ - return NS_OK; -} - -NS_IMETHODIMP -GSugarDownload::OnStatusChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, - nsresult aStatus, const PRUnichar *aMessage) -{ - return NS_OK; -} - -NS_IMETHODIMP -GSugarDownload::OnSecurityChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRUint32 state) -{ - return NS_OK; -} diff --git a/lib/src/sugar-browser-chandler.c b/lib/src/sugar-browser-chandler.c deleted file mode 100644 index a65519a..0000000 --- a/lib/src/sugar-browser-chandler.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "sugar-marshal.h" -#include "sugar-browser-chandler.h" - -enum { - HANDLE_CONTENT, - LAST_SIGNAL -}; -static guint signals[LAST_SIGNAL] = { 0 }; - -G_DEFINE_TYPE(SugarBrowserChandler, sugar_browser_chandler, G_TYPE_OBJECT) - -SugarBrowserChandler *browserChandler = NULL; - -static void -sugar_browser_chandler_init(SugarBrowserChandler *browserChandler) -{ -} - -static void -sugar_browser_chandler_class_init(SugarBrowserChandlerClass *browser_chandler_class) -{ - signals[HANDLE_CONTENT] = - g_signal_new ("handle-content", - G_OBJECT_CLASS_TYPE (browser_chandler_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (SugarBrowserChandlerClass, handle_content), - NULL, NULL, - sugar_marshal_VOID__STRING_STRING_STRING, - G_TYPE_NONE, 3, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_STRING); -} - -SugarBrowserChandler * -sugar_get_browser_chandler() -{ - if(browserChandler == NULL) - browserChandler = g_object_new(SUGAR_TYPE_BROWSER_CHANDLER, NULL); - - return browserChandler; -} - -void -sugar_browser_chandler_handle_content (SugarBrowserChandler *browser_chandler, - const char *url, - const char *mime_type, - const char *tmp_file_name) -{ - g_signal_emit(browser_chandler, - signals[HANDLE_CONTENT], - 0 /* details */, - url, - mime_type, - tmp_file_name); -} diff --git a/lib/src/sugar-browser-chandler.h b/lib/src/sugar-browser-chandler.h deleted file mode 100644 index cccd983..0000000 --- a/lib/src/sugar-browser-chandler.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef __SUGAR_BROWSER_CHANDLER_H__ -#define __SUGAR_BROWSER_CHANDLER_H__ - -#include -#include - -G_BEGIN_DECLS - -typedef struct _SugarBrowserChandler SugarBrowserChandler; -typedef struct _SugarBrowserChandlerClass SugarBrowserChandlerClass; - -#define SUGAR_TYPE_BROWSER_CHANDLER (sugar_browser_chandler_get_type()) -#define SUGAR_BROWSER_CHANDLER(object) (G_TYPE_CHECK_INSTANCE_CAST((object), SUGAR_TYPE_BROWSER_CHANDLER, SugarBrowserChandler)) -#define SUGAR_BROWSER_CHANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SUGAR_TYPE_BROWSER_CHANDLER, SugarBrowserChandlerClass)) -#define SUGAR_IS_BROWSER_CHANDLER(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), SUGAR_TYPE_BROWSER_CHANDLER)) -#define SUGAR_IS_BROWSER_CHANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SUGAR_TYPE_BROWSER_CHANDLER)) -#define SUGAR_BROWSER_CHANDLER_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), SUGAR_TYPE_BROWSER_CHANDLER, SugarBrowserChandlerClass)) - -struct _SugarBrowserChandler { - GObject base_instance; -}; - -struct _SugarBrowserChandlerClass { - GObjectClass base_class; - - void (* handle_content) (char *url, char *tmp_file_name); - -}; - -GType sugar_browser_chandler_get_type (void); -SugarBrowserChandler *sugar_get_browser_chandler (void); -void sugar_browser_chandler_handle_content (SugarBrowserChandler *chandler, - const char *url, - const char *mime_type, - const char *tmp_file_name); - -G_END_DECLS - -#endif diff --git a/lib/src/sugar-browser.cpp b/lib/src/sugar-browser.cpp index d2ffb8a..dfd05d7 100644 --- a/lib/src/sugar-browser.cpp +++ b/lib/src/sugar-browser.cpp @@ -18,8 +18,8 @@ */ #include "sugar-browser.h" -#include "SugarContentHandler.h" -#include "SugarDownload.h" +#include "GeckoContentHandler.h" +#include "GeckoDownload.h" #include #include @@ -35,8 +35,8 @@ #include #include -NS_GENERIC_FACTORY_CONSTRUCTOR(GSugarContentHandler) -NS_GENERIC_FACTORY_CONSTRUCTOR(GSugarDownload) +NS_GENERIC_FACTORY_CONSTRUCTOR(GeckoContentHandler) +NS_GENERIC_FACTORY_CONSTRUCTOR(GeckoDownload) enum { PROP_0, @@ -50,16 +50,16 @@ enum { static const nsModuleComponentInfo sSugarComponents[] = { { - "Sugar Content Handler", - G_SUGARCONTENTHANDLER_CID, + "Gecko Content Handler", + GECKOCONTENTHANDLER_CID, NS_IHELPERAPPLAUNCHERDLG_CONTRACTID, - GSugarContentHandlerConstructor + GeckoContentHandlerConstructor }, { - "Sugar Download", - G_SUGARDOWNLOAD_CID, + "Gecko Download", + GECKODOWNLOAD_CID, NS_TRANSFER_CONTRACTID, - GSugarDownloadConstructor + GeckoDownloadConstructor } }; @@ -78,7 +78,7 @@ sugar_browser_startup(void) PR_TRUE, getter_AddRefs(file)); NS_ENSURE_TRUE(file, FALSE); - rv = prefService->ReadUserPrefs (file); + rv = prefService->ReadUserPrefs (file); if (NS_FAILED(rv)) { g_warning ("failed to read default preferences, error: %x", rv); return FALSE; diff --git a/lib/src/sugar-download-manager.c b/lib/src/sugar-download-manager.c new file mode 100644 index 0000000..ead3bc8 --- /dev/null +++ b/lib/src/sugar-download-manager.c @@ -0,0 +1,165 @@ +#include "sugar-marshal.h" +#include "sugar-download.h" +#include "sugar-download-manager.h" + +enum { + DOWNLOAD_STARTED, + DOWNLOAD_COMPLETED, + DOWNLOAD_CANCELLED, + DOWNLOAD_PROGRESS, + LAST_SIGNAL +}; +static guint signals[LAST_SIGNAL] = { 0 }; + +static void sugar_download_manager_finalize (GObject *object); +static void sugar_download_remove_download (gpointer key, gpointer value, gpointer user_data); + +G_DEFINE_TYPE (SugarDownloadManager, sugar_download_manager, G_TYPE_OBJECT) + +SugarDownloadManager *DownloadManager = NULL; + +static void +sugar_download_manager_init (SugarDownloadManager *download_manager) +{ + download_manager->downloads = g_hash_table_new (g_str_hash, g_str_equal); +} + +static void +sugar_download_manager_class_init (SugarDownloadManagerClass *download_manager_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (download_manager_class); + + gobject_class->finalize = sugar_download_manager_finalize; + + signals[DOWNLOAD_STARTED] = + g_signal_new ("download-started", + G_OBJECT_CLASS_TYPE (download_manager_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SugarDownloadManagerClass, handle_content), + NULL, NULL, + sugar_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + signals[DOWNLOAD_COMPLETED] = + g_signal_new ("download-completed", + G_OBJECT_CLASS_TYPE (download_manager_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SugarDownloadManagerClass, handle_content), + NULL, NULL, + sugar_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + signals[DOWNLOAD_CANCELLED] = + g_signal_new ("download-cancelled", + G_OBJECT_CLASS_TYPE (download_manager_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SugarDownloadManagerClass, handle_content), + NULL, NULL, + sugar_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); + + signals[DOWNLOAD_PROGRESS] = + g_signal_new ("download-progress", + G_OBJECT_CLASS_TYPE (download_manager_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SugarDownloadManagerClass, handle_content), + NULL, NULL, + sugar_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + G_TYPE_OBJECT); +} + +static void +sugar_download_manager_finalize (GObject *object) +{ + SugarDownloadManager *download_manager = SUGAR_DOWNLOAD_MANAGER (object); + g_hash_table_foreach (download_manager->downloads, sugar_download_remove_download, NULL); + g_hash_table_destroy (download_manager->downloads); +} + +static void +sugar_download_remove_download (gpointer key, gpointer value, gpointer user_data) +{ + g_free (value); +} + +SugarDownloadManager * +sugar_get_download_manager () +{ + if (DownloadManager == NULL) + DownloadManager = g_object_new (SUGAR_TYPE_DOWNLOAD_MANAGER, NULL); + + return DownloadManager; +} + +void +sugar_download_manager_download_started (SugarDownloadManager *download_manager, + const char *url, + const char *mime_type, + const char *file_name) +{ + SugarDownload *download = (SugarDownload *) g_hash_table_lookup ( + download_manager->downloads, + file_name); + + g_return_if_fail (download == NULL); + + download = g_object_new (SUGAR_TYPE_DOWNLOAD, NULL); + sugar_download_set_url (download, url); + sugar_download_set_mime_type (download, mime_type); + sugar_download_set_file_name (download, file_name); + + g_hash_table_insert (download_manager->downloads, + (gpointer)file_name, + download); + + g_signal_emit (download_manager, signals[DOWNLOAD_STARTED], 0, download); +} + +void +sugar_download_manager_download_completed (SugarDownloadManager *download_manager, + const char *file_name) +{ + SugarDownload *download = (SugarDownload *) g_hash_table_lookup ( + download_manager->downloads, + file_name); + + g_return_if_fail (download); + + g_signal_emit (download_manager, signals[DOWNLOAD_COMPLETED], 0, download); + + g_hash_table_remove (download_manager->downloads, file_name); +} + +void sugar_download_manager_download_cancelled (SugarDownloadManager *download_manager, + const char *file_name) +{ + SugarDownload *download = (SugarDownload *) g_hash_table_lookup ( + download_manager->downloads, + file_name); + + g_return_if_fail (download); + + g_signal_emit (download_manager, signals[DOWNLOAD_CANCELLED], 0, download); + + g_hash_table_remove (download_manager->downloads, file_name); +} + +void +sugar_download_manager_update_progress (SugarDownloadManager *download_manager, + const char *file_name, + const int percent) +{ + SugarDownload *download = (SugarDownload *) g_hash_table_lookup ( + download_manager->downloads, + file_name); + + g_return_if_fail (download); + + sugar_download_set_percent (download, percent); + + g_signal_emit (download_manager, signals [DOWNLOAD_PROGRESS], 0, download); +} diff --git a/lib/src/sugar-download-manager.h b/lib/src/sugar-download-manager.h new file mode 100644 index 0000000..c58436b --- /dev/null +++ b/lib/src/sugar-download-manager.h @@ -0,0 +1,57 @@ +#ifndef __SUGAR_DOWNLOAD_MANAGER_H__ +#define __SUGAR_DOWNLOAD_MANAGER_H__ + +#include +#include + +G_BEGIN_DECLS + +typedef struct _SugarDownloadManager SugarDownloadManager; +typedef struct _SugarDownloadManagerClass SugarDownloadManagerClass; + +#define SUGAR_TYPE_DOWNLOAD_MANAGER (sugar_download_manager_get_type()) +#define SUGAR_DOWNLOAD_MANAGER(object) (G_TYPE_CHECK_INSTANCE_CAST((object), SUGAR_TYPE_DOWNLOAD_MANAGER, SugarDownloadManager)) +#define SUGAR_DOWNLOAD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SUGAR_TYPE_DOWNLOAD_MANAGER, SugarDownloadManagerClass)) +#define SUGAR_IS_DOWNLOAD_MANAGER(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), SUGAR_TYPE_DOWNLOAD_MANAGER)) +#define SUGAR_IS_DOWNLOAD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SUGAR_TYPE_DOWNLOAD_MANAGER)) +#define SUGAR_DOWNLOAD_MANAGER_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), SUGAR_TYPE_DOWNLOAD_MANAGER, SugarDownloadManagerClass)) + +struct _SugarDownloadManager { + GObject base_instance; + + GHashTable *downloads; +}; + +struct _SugarDownloadManagerClass { + GObjectClass base_class; + + void (* handle_content) (char *url, char *tmp_file_name); + +}; + +GType sugar_download_manager_get_type(void); + +SugarDownloadManager *sugar_get_download_manager(void); + +void sugar_download_manager_download_started( + SugarDownloadManager *download_manager, + const char *url, + const char *mime_type, + const char *tmp_file_name); + +void sugar_download_manager_download_completed( + SugarDownloadManager *download_manager, + const char *tmp_file_name); + +void sugar_download_manager_download_cancelled( + SugarDownloadManager *download_manager, + const char *tmp_file_name); + +void sugar_download_manager_update_progress( + SugarDownloadManager *download_manager, + const char *tmp_file_name, + const int percent); + +G_END_DECLS + +#endif diff --git a/lib/src/sugar-download.c b/lib/src/sugar-download.c new file mode 100644 index 0000000..01ad809 --- /dev/null +++ b/lib/src/sugar-download.c @@ -0,0 +1,108 @@ +#include "sugar-download.h" + +static void sugar_download_finalize (GObject *object); + +G_DEFINE_TYPE (SugarDownload, sugar_download, G_TYPE_OBJECT) + +static void +sugar_download_init (SugarDownload *download) +{ + download->file_name = NULL; + download->url = NULL; + download->mime_type = NULL; + download->percent = 0; +} + +static void +sugar_download_class_init (SugarDownloadClass *download_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (download_class); + + gobject_class->finalize = sugar_download_finalize; +} + +void +sugar_download_set_file_name (SugarDownload *download, const gchar *file_name) +{ + gchar *new_file_name; + + g_return_if_fail (SUGAR_IS_DOWNLOAD (download)); + + new_file_name = g_strdup (file_name); + g_free (download->file_name); + download->file_name = new_file_name; +} + +void +sugar_download_set_url (SugarDownload *download, const gchar *url) +{ + gchar *new_url; + + g_return_if_fail (SUGAR_IS_DOWNLOAD (download)); + + new_url = g_strdup (url); + g_free (download->url); + download->url = new_url; +} + +void +sugar_download_set_mime_type (SugarDownload *download, const gchar *mime_type) +{ + gchar *new_mime_type; + + g_return_if_fail (SUGAR_IS_DOWNLOAD (download)); + + new_mime_type = g_strdup (mime_type); + g_free (download->mime_type); + download->mime_type = new_mime_type; +} + +void +sugar_download_set_percent (SugarDownload *download, const gint percent) +{ + g_return_if_fail (SUGAR_IS_DOWNLOAD (download)); + + download->percent = percent; +} + +const gchar * +sugar_download_get_file_name (SugarDownload *download) +{ + g_return_val_if_fail (SUGAR_IS_DOWNLOAD (download), NULL); + + return download->file_name; +} + +const gchar * +sugar_download_get_url (SugarDownload *download) +{ + g_return_val_if_fail (SUGAR_IS_DOWNLOAD (download), NULL); + + return download->url; +} + +const gchar * +sugar_download_get_mime_type (SugarDownload *download) +{ + g_return_val_if_fail (SUGAR_IS_DOWNLOAD (download), NULL); + + return download->mime_type; +} + +gint +sugar_download_get_percent (SugarDownload *download) +{ + g_return_val_if_fail (SUGAR_IS_DOWNLOAD (download), -1); + + return download->percent; +} + +static void +sugar_download_finalize (GObject *object) +{ + SugarDownload *download = SUGAR_DOWNLOAD (object); + + g_free (download->file_name); + g_free (download->url); + g_free (download->mime_type); +} diff --git a/lib/src/sugar-download.h b/lib/src/sugar-download.h new file mode 100644 index 0000000..ac3760b --- /dev/null +++ b/lib/src/sugar-download.h @@ -0,0 +1,50 @@ +#ifndef __SUGAR_DOWNLOAD_H__ +#define __SUGAR_DOWNLOAD_H__ + +#include +#include + +G_BEGIN_DECLS + +typedef struct _SugarDownload SugarDownload; +typedef struct _SugarDownloadClass SugarDownloadClass; + +#define SUGAR_TYPE_DOWNLOAD (sugar_download_get_type()) +#define SUGAR_DOWNLOAD(object) (G_TYPE_CHECK_INSTANCE_CAST((object), SUGAR_TYPE_DOWNLOAD, SugarDownload)) +#define SUGAR_DOWNLOAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SUGAR_TYPE_DOWNLOAD, SugarDownloadClass)) +#define SUGAR_IS_DOWNLOAD(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), SUGAR_TYPE_DOWNLOAD)) +#define SUGAR_IS_DOWNLOAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SUGAR_TYPE_DOWNLOAD)) +#define SUGAR_DOWNLOAD_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), SUGAR_TYPE_DOWNLOAD, SugarDownloadClass)) + +struct _SugarDownload { + GObject base_instance; + + gchar *file_name; + gchar *url; + gchar *mime_type; + gint percent; +}; + +struct _SugarDownloadClass { + GObjectClass base_class; +}; + +GType sugar_download_get_type(void); + +void sugar_download_set_file_name (SugarDownload *download, + const gchar *file_name); +void sugar_download_set_url (SugarDownload *download, + const gchar *url); +void sugar_download_set_mime_type (SugarDownload *download, + const gchar *mime_type); +void sugar_download_set_percent (SugarDownload *download, + const gint percent); + +const gchar *sugar_download_get_file_name (SugarDownload *download); +const gchar *sugar_download_get_url (SugarDownload *download); +const gchar *sugar_download_get_mime_type (SugarDownload *download); +gint sugar_download_get_percent (SugarDownload *download); + +G_END_DECLS + +#endif /* __SUGAR_DOWNLOAD_H__ */ diff --git a/lib/src/sugar-marshal.list b/lib/src/sugar-marshal.list index 5a0120f..de45d7a 100644 --- a/lib/src/sugar-marshal.list +++ b/lib/src/sugar-marshal.list @@ -1,3 +1,3 @@ VOID:OBJECT,STRING,LONG,LONG VOID:OBJECT,LONG -VOID:STRING,STRING,STRING +VOID:OBJECT diff --git a/services/Makefile.am b/services/Makefile.am index 1bae7e7..2a0fe5a 100644 --- a/services/Makefile.am +++ b/services/Makefile.am @@ -1 +1 @@ -SUBDIRS = presence nm +SUBDIRS = presence nm clipboard diff --git a/services/clipboard/ClipboardService.py b/services/clipboard/ClipboardService.py new file mode 100644 index 0000000..7389c58 --- /dev/null +++ b/services/clipboard/ClipboardService.py @@ -0,0 +1,76 @@ +# vi: ts=4 ai noet +# +# Copyright (C) 2006, Red Hat, Inc. +# +# This program 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. +# +# This program 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 more 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 + +import logging +import gobject +import dbus +import dbus.service +from sugar import env + +class ClipboardDBusServiceHelper(dbus.service.Object): + + _CLIPBOARD_DBUS_INTERFACE = "org.laptop.Clipboard" + _CLIPBOARD_OBJECT_PATH = "/org/laptop/Clipboard" + + def __init__(self, parent): + self._parent = parent + + bus = dbus.SessionBus() + bus_name = dbus.service.BusName(self._CLIPBOARD_DBUS_INTERFACE, bus=bus) + dbus.service.Object.__init__(self, bus_name, self._CLIPBOARD_OBJECT_PATH) + + @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, + in_signature="sss", out_signature="") + def add_object(self, name, mimeType, fileName): + self.object_added(name, mimeType, fileName) + logging.debug('Added object of type ' + mimeType + ' with path at ' + fileName) + + @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, + in_signature="s", out_signature="") + def delete_object(self, fileName): + self.object_deleted(fileName) + logging.debug('Deleted object with path at ' + fileName) + + @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, + in_signature="si", out_signature="") + def set_object_state(self, fileName, percent): + logging.debug('Changed object with path at ' + fileName + ' with percent ' + str(percent)) + self.object_state_changed(fileName, percent) + + @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="sss") + def object_added(self, name, mimeType, fileName): + pass + + @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="s") + def object_deleted(self, fileName): + pass + + @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="si") + def object_state_changed(self, fileName, percent): + pass + +class ClipboardService(object): + def __init__(self): + self._dbus_helper = ClipboardDBusServiceHelper(self) + + def run(self): + loop = gobject.MainLoop() + try: + loop.run() + except KeyboardInterrupt: + print 'Ctrl+C pressed, exiting...' diff --git a/services/clipboard/Makefile.am b/services/clipboard/Makefile.am new file mode 100644 index 0000000..ccc54f2 --- /dev/null +++ b/services/clipboard/Makefile.am @@ -0,0 +1,15 @@ +servicedir = $(datadir)/sugar/services +service_in_files = org.laptop.Clipboard.service.in +service_DATA = $(service_in_files:.service.in=.service) + +$(service_DATA): $(service_in_files) Makefile + @sed -e "s|\@bindir\@|$(bindir)|" $< > $@ + +sugardir = $(pkgdatadir)/services/clipboard +sugar_PYTHON = \ + __init__.py \ + ClipboardService.py + +bin_SCRIPTS = sugar-clipboard + +EXTRA_DIST = $(service_in_files) $(bin_SCRIPTS) diff --git a/services/clipboard/__init__.py b/services/clipboard/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/services/clipboard/__init__.py diff --git a/services/clipboard/org.laptop.Clipboard.service.in b/services/clipboard/org.laptop.Clipboard.service.in new file mode 100644 index 0000000..b38bf2b --- /dev/null +++ b/services/clipboard/org.laptop.Clipboard.service.in @@ -0,0 +1,4 @@ +[D-BUS Service] +Name = org.laptop.Clipboard +Exec = @bindir@/sugar-clipboard + diff --git a/services/clipboard/sugar-clipboard b/services/clipboard/sugar-clipboard new file mode 100755 index 0000000..bcbd280 --- /dev/null +++ b/services/clipboard/sugar-clipboard @@ -0,0 +1,44 @@ +#!/usr/bin/python +# vi: ts=4 ai noet +# +# Copyright (C) 2006, Red Hat, Inc. +# +# This program 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. +# +# This program 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 more 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 + +import sys +import logging + +from sugar import logger +logger.start('clipboard') + +import gobject +import pygtk +pygtk.require('2.0') + +import dbus.glib + +from sugar import env + +sys.path.insert(0, env.get_services_dir()) + +from clipboard.ClipboardService import ClipboardService + +logging.info('Starting clipboard service.') + +gobject.threads_init() +dbus.glib.threads_init() + +app = ClipboardService() +app.run() diff --git a/shell/view/ClipboardIcon.py b/shell/view/ClipboardIcon.py new file mode 100644 index 0000000..429fc99 --- /dev/null +++ b/shell/view/ClipboardIcon.py @@ -0,0 +1,38 @@ +from sugar.graphics.menuicon import MenuIcon +from view.ClipboardMenu import ClipboardMenu +from sugar.activity import ActivityFactory +from sugar.clipboard import ClipboardService + +class ClipboardIcon(MenuIcon): + + def __init__(self, menu_shell, name, file_name): + MenuIcon.__init__(self, menu_shell, icon_name='activity-xbook') + self._name = name + self._file_name = file_name + self._percent = 0 + self.connect('activated', self._icon_activated_cb) + self._menu = None + + def create_menu(self): + self._menu = ClipboardMenu(self._name, self._percent) + self._menu.connect('action', self._popup_action_cb) + return self._menu + + def set_percent(self, percent): + self._percent = percent + if self._menu: + self._menu.set_percent(percent) + + def _icon_activated_cb(self, icon): + if self._percent == 100: + activity = ActivityFactory.create("org.laptop.sugar.Xbook") + activity.execute("open_document", [self._file_name]) + + def _popup_action_cb(self, popup, action): + self.popdown() + + if action == ClipboardMenu.ACTION_STOP_DOWNLOAD: + raise "Stopping downloads still not implemented." + elif action == ClipboardMenu.ACTION_DELETE: + cb_service = ClipboardService.get_instance() + cb_service.delete_object(self._file_name) diff --git a/shell/view/ClipboardMenu.py b/shell/view/ClipboardMenu.py new file mode 100644 index 0000000..44ca798 --- /dev/null +++ b/shell/view/ClipboardMenu.py @@ -0,0 +1,56 @@ +import gtk +import gobject +import hippo + +from sugar.graphics.menu import Menu +from sugar.graphics.canvasicon import CanvasIcon +from sugar.graphics.ClipboardBubble import ClipboardBubble +from sugar.graphics import style + +class ClipboardMenuItem(ClipboardBubble): + + def __init__(self, percent = 0, stylesheet="clipboard.Bubble"): + ClipboardBubble.__init__(self, percent = percent) + style.apply_stylesheet(self, stylesheet) + +class ClipboardMenu(Menu): + + ACTION_DELETE = 0 + ACTION_SHARE = 1 + ACTION_STOP_DOWNLOAD = 2 + + def __init__(self, name, percent): + Menu.__init__(self, name) + + self._progress_bar = ClipboardMenuItem(percent) + self._root.append(self._progress_bar) + + #icon = CanvasIcon(icon_name='stock-share-mesh') + #self.add_action(icon, ClipboardMenu.ACTION_SHARE) + + self._remove_icon = None + self._stop_icon = None + + self._create_icons(percent) + + def _create_icons(self, percent): + if percent == 100: + if not self._remove_icon: + self._remove_icon = CanvasIcon(icon_name='stock-remove') + self.add_action(self._remove_icon, ClipboardMenu.ACTION_DELETE) + + if self._stop_icon: + self.remove_action(self._stop_icon) + self._stop_icon = None + else: + if not self._stop_icon: + self._stop_icon = CanvasIcon(icon_name='stock-close') + self.add_action(self._stop_icon, ClipboardMenu.ACTION_STOP_DOWNLOAD) + + if self._remove_icon: + self.remove_action(self._remove_icon) + self._remove_icon = None + + def set_percent(self, percent): + self._progress_bar.set_property('percent', percent) + self._create_icons(percent) diff --git a/shell/view/Makefile.am b/shell/view/Makefile.am index bf77593..bd90a2d 100644 --- a/shell/view/Makefile.am +++ b/shell/view/Makefile.am @@ -7,6 +7,8 @@ sugar_PYTHON = \ FirstTimeDialog.py \ BuddyIcon.py \ BuddyMenu.py \ + ClipboardIcon.py \ + ClipboardMenu.py \ OverlayWindow.py \ Shell.py \ dconmanager.py \ diff --git a/shell/view/frame/ClipboardBox.py b/shell/view/frame/ClipboardBox.py new file mode 100644 index 0000000..849c7e0 --- /dev/null +++ b/shell/view/frame/ClipboardBox.py @@ -0,0 +1,42 @@ +import logging +import dbus +import hippo + +from sugar.graphics import style +from view.ClipboardIcon import ClipboardIcon +from sugar.clipboard import ClipboardService + +class ClipboardBox(hippo.CanvasBox): + + def __init__(self, frame, menu_shell): + hippo.CanvasBox.__init__(self) + self._frame = frame + self._menu_shell = menu_shell + self._icons = {} + + cb_service = ClipboardService.get_instance() + cb_service.connect('object-added', self._object_added_cb) + cb_service.connect('object-deleted', self._object_deleted_cb) + cb_service.connect('object-state-changed', self._object_state_changed_cb) + + def _object_added_cb(self, cb_service, name, mimeType, fileName): + icon = ClipboardIcon(self._menu_shell, name, fileName) + style.apply_stylesheet(icon, 'frame.BuddyIcon') + self.append(icon) + self._icons[fileName] = icon + + if not self._frame.is_visible(): + self._frame.show_and_hide(0.1) + + logging.debug('ClipboardBox: ' + fileName + ' was added.') + + def _object_deleted_cb(self, cb_service, fileName): + icon = self._icons[fileName] + self.remove(icon) + del self._icons[fileName] + logging.debug('ClipboardBox: ' + fileName + ' was deleted.') + + def _object_state_changed_cb(self, cb_service, fileName, percent): + icon = self._icons[fileName] + icon.set_percent(percent) + logging.debug('ClipboardBox: ' + fileName + ' state was changed.') diff --git a/shell/view/frame/Frame.py b/shell/view/frame/Frame.py index fd3c11e..37f28cb 100644 --- a/shell/view/frame/Frame.py +++ b/shell/view/frame/Frame.py @@ -23,6 +23,7 @@ from view.frame.ActivitiesBox import ActivitiesBox from view.frame.ZoomBox import ZoomBox from view.frame.overlaybox import OverlayBox from view.frame.FriendsBox import FriendsBox +from view.frame.ClipboardBox import ClipboardBox from view.frame.PanelWindow import PanelWindow from view.frame.notificationtray import NotificationTray from view.frame.shutdownicon import ShutdownIcon @@ -205,7 +206,10 @@ class Frame: root.append(box) # Left panel - self._create_panel(grid, 0, 1, 1, 10) + [menu_shell, root] = self._create_panel(grid, 0, 1, 1, 10) + + box = ClipboardBox(self, menu_shell) + root.append(box) def _create_panel(self, grid, x, y, width, height): panel = PanelWindow() diff --git a/shell/view/frame/Makefile.am b/shell/view/frame/Makefile.am index 81a5406..5d96a23 100644 --- a/shell/view/frame/Makefile.am +++ b/shell/view/frame/Makefile.am @@ -2,6 +2,7 @@ sugardir = $(pkgdatadir)/shell/view/frame sugar_PYTHON = \ __init__.py \ ActivitiesBox.py \ + ClipboardBox.py \ FriendsBox.py \ PanelWindow.py \ Frame.py \ diff --git a/shell/view/stylesheet.py b/shell/view/stylesheet.py index e6c0688..1870f9c 100644 --- a/shell/view/stylesheet.py +++ b/shell/view/stylesheet.py @@ -60,3 +60,21 @@ friends_FriendIcon = { friends_ActivityIcon = { 'size' : style.standard_icon_size } + +clipboard_bubble = { + 'fill-color' : 0x646464FF, + 'stroke-color' : 0x646464FF, + 'progress-color': 0x333333FF, + 'spacing' : style.space_unit, + 'padding' : style.space_unit * 1.5 +} + +clipboard_menu_item_title = { + 'xalign': hippo.ALIGNMENT_START, + 'padding-left': 5, + 'color' : 0xFFFFFFFF, + 'font' : style.get_font_description('Bold', 1.2) +} + +style.register_stylesheet("clipboard.Bubble", clipboard_bubble) +style.register_stylesheet("clipboard.MenuItem.Title", clipboard_menu_item_title) diff --git a/sugar-emulator b/sugar-emulator index 6635a39..a26f0f2 100755 --- a/sugar-emulator +++ b/sugar-emulator @@ -40,6 +40,7 @@ if sourcedir: bin_path = sourcedir bin_path += ':' + os.path.join(sourcedir, 'shell') bin_path += ':' + os.path.join(sourcedir, 'services/presence') + bin_path += ':' + os.path.join(sourcedir, 'services/clipboard') if os.environ.has_key('PATH'): old_path = os.environ['PATH'] @@ -54,6 +55,11 @@ if sourcedir: bin = os.path.join(sourcedir, 'services/presence/sugar-presence-service') setup.write_service('org.laptop.Presence', bin, + env.get_activity_info_dir()) + + bin = os.path.join(sourcedir, + 'services/clipboard/sugar-clipboard') + setup.write_service('org.laptop.Clipboard', bin, env.get_activity_info_dir()) from sugar.emulator import Emulator diff --git a/sugar/Makefile.am b/sugar/Makefile.am index ae2e2ee..905555a 100644 --- a/sugar/Makefile.am +++ b/sugar/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = activity chat graphics p2p presence +SUBDIRS = activity chat clipboard graphics p2p presence sugardir = $(pythondir)/sugar sugar_PYTHON = \ diff --git a/sugar/clipboard/ClipboardService.py b/sugar/clipboard/ClipboardService.py new file mode 100644 index 0000000..7c9dd1f --- /dev/null +++ b/sugar/clipboard/ClipboardService.py @@ -0,0 +1,75 @@ +import dbus +import gobject + +DBUS_SERVICE = "org.laptop.Clipboard" +DBUS_INTERFACE = "org.laptop.Clipboard" +DBUS_PATH = "/org/laptop/Clipboard" + +class ClipboardService(gobject.GObject): + + __gsignals__ = { + 'object-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([str, str, str])), + 'object-deleted': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([str])), + 'object-state-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([str, int])), + } + + def __init__(self): + gobject.GObject.__init__(self) + + self._dbus_service = None + bus = dbus.SessionBus() + bus.add_signal_receiver(self._name_owner_changed_cb, + signal_name="NameOwnerChanged", + dbus_interface="org.freedesktop.DBus") + # Try to register to ClipboardService, if we fail, we'll try later. + try: + self._connect_clipboard_signals() + except dbus.DBusException, exception: + pass + + def _connect_clipboard_signals(self): + bus = dbus.SessionBus() + proxy_obj = bus.get_object(DBUS_SERVICE, DBUS_PATH) + self._dbus_service = dbus.Interface(proxy_obj, DBUS_SERVICE) + self._dbus_service.connect_to_signal('object_added', + self._object_added_cb) + self._dbus_service.connect_to_signal('object_deleted', + self._object_deleted_cb) + self._dbus_service.connect_to_signal('object_state_changed', + self._object_state_changed_cb) + + def _name_owner_changed_cb(self, name, old, new): + if name != DBUS_SERVICE: + return + + if (not old and not len(old)) and (new and len(new)): + # ClipboardService started up + self._connect_clipboard_signals() + + def _object_added_cb(self, name, mimeType, fileName): + self.emit('object-added', name, mimeType, fileName) + + def _object_deleted_cb(self, fileName): + self.emit('object-deleted', fileName) + + def _object_state_changed_cb(self, fileName, percent): + self.emit('object-state-changed', fileName, percent) + + def add_object(self, name, mimeType, fileName): + self._dbus_service.add_object(name, mimeType, fileName) + + def delete_object(self, fileName): + self._dbus_service.delete_object(fileName) + + def set_object_state(self, fileName, percent): + self._dbus_service.set_object_state(fileName, percent) + +_clipboard_service = None +def get_instance(): + global _clipboard_service + if not _clipboard_service: + _clipboard_service = ClipboardService() + return _clipboard_service diff --git a/sugar/clipboard/Makefile.am b/sugar/clipboard/Makefile.am new file mode 100644 index 0000000..dbfabe9 --- /dev/null +++ b/sugar/clipboard/Makefile.am @@ -0,0 +1,5 @@ +sugardir = $(pythondir)/sugar/clipboard +sugar_PYTHON = \ + __init__.py \ + ClipboardService.py + diff --git a/sugar/clipboard/__init__.py b/sugar/clipboard/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/sugar/clipboard/__init__.py diff --git a/sugar/graphics/ClipboardBubble.py b/sugar/graphics/ClipboardBubble.py new file mode 100644 index 0000000..b94fc26 --- /dev/null +++ b/sugar/graphics/ClipboardBubble.py @@ -0,0 +1,131 @@ +# Copyright (C) 2006, Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +#TODO: has to be merged with all the existing bubbles in a generic progress bar widget + +import math + +import gobject +import gtk +import hippo + +class ClipboardBubble(hippo.CanvasBox, hippo.CanvasItem): + __gtype_name__ = 'ClipboardBubble' + + __gproperties__ = { + 'fill-color': (object, None, None, + gobject.PARAM_READWRITE), + 'stroke-color': (object, None, None, + gobject.PARAM_READWRITE), + 'progress-color': (object, None, None, + gobject.PARAM_READWRITE), + 'percent' : (object, None, None, + gobject.PARAM_READWRITE), + } + + def __init__(self, **kwargs): + self._stroke_color = 0xFFFFFFFF + self._fill_color = 0xFFFFFFFF + self._progress_color = 0x000000FF + self._percent = 0 + self._radius = 8 + + hippo.CanvasBox.__init__(self, **kwargs) + + def do_set_property(self, pspec, value): + if pspec.name == 'fill-color': + self._fill_color = value + self.emit_paint_needed(0, 0, -1, -1) + elif pspec.name == 'stroke-color': + self._stroke_color = value + self.emit_paint_needed(0, 0, -1, -1) + elif pspec.name == 'progress-color': + self._progress_color = value + self.emit_paint_needed(0, 0, -1, -1) + elif pspec.name == 'percent': + self._percent = value + self.emit_paint_needed(0, 0, -1, -1) + + def do_get_property(self, pspec): + if pspec.name == 'fill-color': + return self._fill_color + elif pspec.name == 'stroke-color': + return self._stroke_color + elif pspec.name == 'progress-color': + return self._progress_color + elif pspec.name == 'percent': + return self._percent + + def _int_to_rgb(self, int_color): + red = (int_color >> 24) & 0x000000FF + green = (int_color >> 16) & 0x000000FF + blue = (int_color >> 8) & 0x000000FF + alpha = int_color & 0x000000FF + return (red / 255.0, green / 255.0, blue / 255.0) + + def do_paint_below_children(self, cr, damaged_box): + [width, height] = self.get_allocation() + + line_width = 3.0 + x = line_width + y = line_width + width -= line_width * 2 + height -= line_width * 2 + + self._paint_ellipse(cr, x, y, width, height, self._fill_color) + + color = self._int_to_rgb(self._stroke_color) + cr.set_source_rgb(*color) + cr.set_line_width(line_width) + cr.stroke(); + + self._paint_progress_bar(cr, x, y, width, height, line_width) + + def _paint_progress_bar(self, cr, x, y, width, height, line_width): + prog_x = x + line_width + prog_y = y + line_width + prog_width = (width - (line_width * 2)) * (self._percent / 100.0) + prog_height = (height - (line_width * 2)) + + self._paint_ellipse(cr, prog_x, prog_y, width, height, self._progress_color) + + def _paint_ellipse(self, cr, x, y, width, height, fill_color): + cr.move_to(x + self._radius, y) + cr.arc(x + width - self._radius, + y + self._radius, + self._radius, + math.pi * 1.5, + math.pi * 2) + cr.arc(x + width - self._radius, + x + height - self._radius, + self._radius, + 0, + math.pi * 0.5) + cr.arc(x + self._radius, + y + height - self._radius, + self._radius, + math.pi * 0.5, + math.pi) + cr.arc(x + self._radius, + y + self._radius, + self._radius, + math.pi, + math.pi * 1.5); + + color = self._int_to_rgb(fill_color) + cr.set_source_rgb(*color) + cr.fill_preserve(); diff --git a/sugar/graphics/Makefile.am b/sugar/graphics/Makefile.am index 83b0920..9828c8c 100644 --- a/sugar/graphics/Makefile.am +++ b/sugar/graphics/Makefile.am @@ -4,6 +4,7 @@ sugar_PYTHON = \ bubble.py \ canvasicon.py \ colors.py \ + ClipboardBubble.py \ grid.py \ iconcolor.py \ menu.py \ diff --git a/sugar/graphics/menu.py b/sugar/graphics/menu.py index dfc0e47..508dbb0 100644 --- a/sugar/graphics/menu.py +++ b/sugar/graphics/menu.py @@ -97,6 +97,9 @@ class Menu(gtk.Window): icon.connect('activated', self._action_clicked_cb, action_id) self._action_box.append(icon) + def remove_action(self, icon): + self._action_box.remove(icon) + def _item_clicked_cb(self, icon, event, action): self.emit('action', action) -- cgit v0.9.1