From 23565cfd483e38d3a64cd5980deea47cccb28581 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 1 Nov 2006 19:43:59 +0100 Subject: [PATCH] First version of the ClipboardService. Added support for showing the progress of a pdf download in the clipboard. --- activities/web/webactivity.py | 32 ++++- configure.ac | 1 + lib/src/SugarDownload.cpp | 52 ++++--- lib/src/sugar-browser-chandler.c | 85 +++++++++-- lib/src/sugar-browser-chandler.h | 15 +- lib/src/sugar-browser.cpp | 2 +- lib/src/sugar-marshal.list | 2 + services/Makefile.am | 2 +- services/clipboard/ClipboardService.py | 76 ++++++++++ services/clipboard/Makefile.am | 15 ++ services/clipboard/__init__.py | 0 .../clipboard/org.laptop.Clipboard.service.in | 4 + services/clipboard/sugar-clipboard | 44 ++++++ shell/view/ClipboardIcon.py | 33 +++++ shell/view/ClipboardMenu.py | 58 ++++++++ shell/view/Makefile.am | 2 + shell/view/frame/ClipboardBox.py | 61 ++++++++ shell/view/frame/Frame.py | 6 +- shell/view/frame/Makefile.am | 1 + sugar-emulator | 6 + sugar/graphics/ClipboardBubble.py | 133 ++++++++++++++++++ 21 files changed, 593 insertions(+), 37 deletions(-) create mode 100644 services/clipboard/ClipboardService.py create mode 100644 services/clipboard/Makefile.am create mode 100644 services/clipboard/__init__.py create mode 100644 services/clipboard/org.laptop.Clipboard.service.in create mode 100755 services/clipboard/sugar-clipboard create mode 100644 shell/view/ClipboardIcon.py create mode 100644 shell/view/ClipboardMenu.py create mode 100644 shell/view/frame/ClipboardBox.py create mode 100644 sugar/graphics/ClipboardBubble.py diff --git a/activities/web/webactivity.py b/activities/web/webactivity.py index 200bd39d..c36e2acf 100644 --- a/activities/web/webactivity.py +++ b/activities/web/webactivity.py @@ -18,6 +18,7 @@ from gettext import gettext as _ import gtk import gtkmozembed import logging +import dbus import _sugar from sugar.activity import ActivityFactory @@ -105,11 +106,34 @@ def start(): style.load_stylesheet(web.stylesheet) chandler = _sugar.get_browser_chandler() - chandler.connect('handle-content', handle_content_cb) + chandler.connect('download-started', download_started_cb) + chandler.connect('download-completed', download_completed_cb) + chandler.connect('download-cancelled', download_started_cb) + chandler.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(chandler, url, mimeType, tmpFileName): + bus = dbus.SessionBus() + proxy_obj = bus.get_object('org.laptop.Clipboard', '/org/laptop/Clipboard') + iface = dbus.Interface(proxy_obj, 'org.laptop.Clipboard') + iface.add_object(mimeType, tmpFileName) + +def download_completed_cb(chandler, tmpFileName): + bus = dbus.SessionBus() + proxy_obj = bus.get_object('org.laptop.Clipboard', '/org/laptop/Clipboard') + iface = dbus.Interface(proxy_obj, 'org.laptop.Clipboard') + iface.update_object_state(tmpFileName, 100) + +def download_cancelled_cb(chandler, tmpFileName): + bus = dbus.SessionBus() + proxy_obj = bus.get_object('org.laptop.Clipboard', '/org/laptop/Clipboard') + iface = dbus.Interface(proxy_obj, 'org.laptop.Clipboard') + iface.delete_object(tmpFileName, 100) + +def download_progress_cb(chandler, tmpFileName, progress): + bus = dbus.SessionBus() + proxy_obj = bus.get_object('org.laptop.Clipboard', '/org/laptop/Clipboard') + iface = dbus.Interface(proxy_obj, 'org.laptop.Clipboard') + iface.update_object_state(tmpFileName, progress) diff --git a/configure.ac b/configure.ac index accf1e47..a9fc19aa 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 diff --git a/lib/src/SugarDownload.cpp b/lib/src/SugarDownload.cpp index 9c68b8f7..62ca5ac6 100644 --- a/lib/src/SugarDownload.cpp +++ b/lib/src/SugarDownload.cpp @@ -35,27 +35,39 @@ GSugarDownload::Init (nsIURI *aSource, NS_IMETHODIMP GSugarDownload::OnStateChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRUint32 aStateFlags, nsresult aStatus) -{ - nsCString url; - nsCString mimeType; - nsCString targetURI; - - if ((((aStateFlags & STATE_IS_REQUEST) && +{ + SugarBrowserChandler *browser_chandler = sugar_get_browser_chandler(); + + if (((aStateFlags & STATE_IS_REQUEST) && (aStateFlags & STATE_IS_NETWORK) && - (aStateFlags & STATE_STOP)) || - aStateFlags == STATE_STOP) && - NS_SUCCEEDED (aStatus)) { + (aStateFlags & STATE_START)) || + aStateFlags == STATE_START) { + + nsCString url; + nsCString mimeType; 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()); + sugar_browser_chandler_download_started(browser_chandler, + url.get(), + mimeType.get(), + mTargetFileName.get()); + + } else if (((aStateFlags & STATE_IS_REQUEST) && + (aStateFlags & STATE_IS_NETWORK) && + (aStateFlags & STATE_STOP)) || + aStateFlags == STATE_STOP) { + + if (NS_SUCCEEDED (aStatus)) { + sugar_browser_chandler_download_completed(browser_chandler, + mTargetFileName.get()); + } else { + sugar_browser_chandler_download_cancelled(browser_chandler, + mTargetFileName.get()); + } } - + return NS_OK; } @@ -79,7 +91,15 @@ GSugarDownload::OnProgressChange64 (nsIWebProgress *aWebProgress, PRInt64 aMaxSelfProgress, PRInt64 aCurTotalProgress, PRInt64 aMaxTotalProgress) -{ +{ + SugarBrowserChandler *browser_chandler = sugar_get_browser_chandler(); + PRInt32 percentComplete = + (PRInt32)(((float)aCurSelfProgress / (float)aMaxSelfProgress) * 100.0); + + sugar_browser_chandler_update_progress(browser_chandler, + mTargetFileName.get(), + percentComplete); + return NS_OK; } diff --git a/lib/src/sugar-browser-chandler.c b/lib/src/sugar-browser-chandler.c index a65519ac..da6e9d02 100644 --- a/lib/src/sugar-browser-chandler.c +++ b/lib/src/sugar-browser-chandler.c @@ -2,7 +2,10 @@ #include "sugar-browser-chandler.h" enum { - HANDLE_CONTENT, + DOWNLOAD_STARTED, + DOWNLOAD_COMPLETED, + DOWNLOAD_CANCELLED, + DOWNLOAD_PROGRESS, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -19,8 +22,8 @@ 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", + signals[DOWNLOAD_STARTED] = + g_signal_new ("download-started", G_OBJECT_CLASS_TYPE (browser_chandler_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SugarBrowserChandlerClass, handle_content), @@ -30,6 +33,37 @@ sugar_browser_chandler_class_init(SugarBrowserChandlerClass *browser_chandler_cl G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + + signals[DOWNLOAD_COMPLETED] = + g_signal_new ("download-completed", + G_OBJECT_CLASS_TYPE (browser_chandler_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SugarBrowserChandlerClass, handle_content), + NULL, NULL, + sugar_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + signals[DOWNLOAD_CANCELLED] = + g_signal_new ("download-cancelled", + G_OBJECT_CLASS_TYPE (browser_chandler_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SugarBrowserChandlerClass, handle_content), + NULL, NULL, + sugar_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + signals[DOWNLOAD_PROGRESS] = + g_signal_new ("download-progress", + G_OBJECT_CLASS_TYPE (browser_chandler_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (SugarBrowserChandlerClass, handle_content), + NULL, NULL, + sugar_marshal_VOID__STRING_INT, + G_TYPE_NONE, 2, + G_TYPE_STRING, + G_TYPE_INT); } SugarBrowserChandler * @@ -42,15 +76,46 @@ sugar_get_browser_chandler() } void -sugar_browser_chandler_handle_content (SugarBrowserChandler *browser_chandler, - const char *url, - const char *mime_type, - const char *tmp_file_name) -{ +sugar_browser_chandler_download_started (SugarBrowserChandler *browser_chandler, + const char *url, + const char *mime_type, + const char *tmp_file_name) +{ g_signal_emit(browser_chandler, - signals[HANDLE_CONTENT], + signals[DOWNLOAD_STARTED], 0 /* details */, url, mime_type, - tmp_file_name); + tmp_file_name); +} + +void +sugar_browser_chandler_download_completed (SugarBrowserChandler *browser_chandler, + const char *tmp_file_name) +{ + g_signal_emit(browser_chandler, + signals[DOWNLOAD_COMPLETED], + 0 /* details */, + tmp_file_name); +} + +void sugar_browser_chandler_download_cancelled (SugarBrowserChandler *browser_chandler, + const char *tmp_file_name) +{ + g_signal_emit(browser_chandler, + signals[DOWNLOAD_CANCELLED], + 0 /* details */, + tmp_file_name); +} + +void +sugar_browser_chandler_update_progress (SugarBrowserChandler *browser_chandler, + const char *tmp_file_name, + const int percent) +{ + g_signal_emit(browser_chandler, + signals[DOWNLOAD_PROGRESS], + 0 /* details */, + tmp_file_name, + percent); } diff --git a/lib/src/sugar-browser-chandler.h b/lib/src/sugar-browser-chandler.h index cccd9831..f851b70a 100644 --- a/lib/src/sugar-browser-chandler.h +++ b/lib/src/sugar-browser-chandler.h @@ -29,10 +29,17 @@ struct _SugarBrowserChandlerClass { 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); +void sugar_browser_chandler_download_started (SugarBrowserChandler *chandler, + const char *url, + const char *mime_type, + const char *tmp_file_name); +void sugar_browser_chandler_download_completed (SugarBrowserChandler *chandler, + const char *tmp_file_name); +void sugar_browser_chandler_download_cancelled (SugarBrowserChandler *chandler, + const char *tmp_file_name); +void sugar_browser_chandler_update_progress (SugarBrowserChandler *chandler, + const char *tmp_file_name, + const int percent); G_END_DECLS diff --git a/lib/src/sugar-browser.cpp b/lib/src/sugar-browser.cpp index d2ffb8ad..f787f192 100644 --- a/lib/src/sugar-browser.cpp +++ b/lib/src/sugar-browser.cpp @@ -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-marshal.list b/lib/src/sugar-marshal.list index 5a0120f3..4d987f8d 100644 --- a/lib/src/sugar-marshal.list +++ b/lib/src/sugar-marshal.list @@ -1,3 +1,5 @@ VOID:OBJECT,STRING,LONG,LONG VOID:OBJECT,LONG VOID:STRING,STRING,STRING +VOID:STRING,INT +VOID:STRING diff --git a/services/Makefile.am b/services/Makefile.am index 1bae7e78..2a0fe5ae 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 00000000..35ac3a24 --- /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 + +_CLIPBOARD_SERVICE = "org.laptop.Clipboard" +_CLIPBOARD_DBUS_INTERFACE = "org.laptop.Clipboard" +_CLIPBOARD_OBJECT_PATH = "/org/laptop/Clipboard" + +class ClipboardDBusServiceHelper(dbus.service.Object): + def __init__(self, parent): + self._parent = parent + + bus = dbus.SessionBus() + bus_name = dbus.service.BusName(_CLIPBOARD_DBUS_INTERFACE, bus=bus) + dbus.service.Object.__init__(self, bus_name, _CLIPBOARD_OBJECT_PATH) + + @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, + in_signature="ss", out_signature="") + def add_object(self, mimeType, fileName): + logging.debug('Added object of type ' + mimeType + ' with path at ' + fileName) + self.object_added(mimeType, fileName) + + @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, + in_signature="s", out_signature="") + def delete_object(self, fileName): + logging.debug('Deleted object with path at ' + fileName) + self.object_deleted(fileName) + + @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, + in_signature="si", out_signature="") + def update_object_state(self, fileName, percent): + logging.debug('Updated object with path at ' + fileName + ' with percent ' + str(percent)) + self.object_state_updated(fileName, percent) + + @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="ss") + def object_added(self, 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_updated(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 00000000..ccc54f27 --- /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 00000000..e69de29b diff --git a/services/clipboard/org.laptop.Clipboard.service.in b/services/clipboard/org.laptop.Clipboard.service.in new file mode 100644 index 00000000..b38bf2b1 --- /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 00000000..bcbd2807 --- /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 00000000..9a9b6f46 --- /dev/null +++ b/shell/view/ClipboardIcon.py @@ -0,0 +1,33 @@ +from sugar.graphics.menuicon import MenuIcon +from view.ClipboardMenu import ClipboardMenu +from sugar.activity import ActivityFactory + +class ClipboardIcon(MenuIcon): + def __init__(self, menu_shell, file_name): + MenuIcon.__init__(self, menu_shell, icon_name='stock-written-doc') + 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._file_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): + 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_DELETE: +# activity = self._shell.get_current_activity() +# activity.invite(ps_buddy) + pass diff --git a/shell/view/ClipboardMenu.py b/shell/view/ClipboardMenu.py new file mode 100644 index 00000000..16a03847 --- /dev/null +++ b/shell/view/ClipboardMenu.py @@ -0,0 +1,58 @@ +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 + +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) + +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, file_name, percent): + Menu.__init__(self, file_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) + + if percent == 100: + icon = CanvasIcon(icon_name='stock-remove') + self.add_action(icon, ClipboardMenu.ACTION_DELETE) + else: + icon = CanvasIcon(icon_name='stock-close') + self.add_action(icon, ClipboardMenu.ACTION_STOP_DOWNLOAD) + + def set_percent(self, percent): + self._progress_bar.set_property('percent', percent) + diff --git a/shell/view/Makefile.am b/shell/view/Makefile.am index 0e3951aa..4438f9b8 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 \ stylesheet.py diff --git a/shell/view/frame/ClipboardBox.py b/shell/view/frame/ClipboardBox.py new file mode 100644 index 00000000..9eae973e --- /dev/null +++ b/shell/view/frame/ClipboardBox.py @@ -0,0 +1,61 @@ +import logging +import dbus +import hippo + +from sugar.graphics import style +from view.ClipboardIcon import ClipboardIcon + +class ClipboardBox(hippo.CanvasBox): + + _CLIPBOARD_SERVICE = "org.laptop.Clipboard" + _CLIPBOARD_OBJECT_PATH = "/org/laptop/Clipboard" + + def __init__(self, shell, menu_shell): + hippo.CanvasBox.__init__(self) + self._shell = shell + self._menu_shell = menu_shell + self._icons = {} + + 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(self._CLIPBOARD_SERVICE, self._CLIPBOARD_OBJECT_PATH) + iface = dbus.Interface(proxy_obj, self._CLIPBOARD_SERVICE) + iface.connect_to_signal('object_added', self.object_added_callback) + iface.connect_to_signal('object_deleted', self.object_deleted_callback) + iface.connect_to_signal('object_state_updated', self.object_state_updated_callback) + + def name_owner_changed_cb(self, name, old, new): + if name != self._CLIPBOARD_SERVICE: + return + if (not old and not len(old)) and (new and len(new)): + # ClipboardService started up + self._connect_clipboard_signals() + + def object_added_callback(self, mimeType, fileName): + icon = ClipboardIcon(self._menu_shell, fileName) + style.apply_stylesheet(icon, 'frame.BuddyIcon') + self.append(icon) + self._icons[fileName] = icon + + logging.debug('ClipboardBox: ' + fileName + ' was added.') + + def object_deleted_callback(self, fileName): + icon = self._icons[fileName] + self.remove(icon) + self._icons.remove(icon) + logging.debug('ClipboardBox: ' + fileName + ' was deleted.') + + def object_state_updated_callback(self, fileName, percent): + icon = self._icons[fileName] + icon.set_percent(percent) + logging.debug('ClipboardBox: ' + fileName + ' state was updated.') diff --git a/shell/view/frame/Frame.py b/shell/view/frame/Frame.py index 80fb4d17..ad6a3908 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 sugar.graphics.timeline import Timeline @@ -198,7 +199,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._shell, 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 eec6d0fb..23e24af4 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/sugar-emulator b/sugar-emulator index 9b629d4b..b8a64de6 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/graphics/ClipboardBubble.py b/sugar/graphics/ClipboardBubble.py new file mode 100644 index 00000000..4c1da587 --- /dev/null +++ b/sugar/graphics/ClipboardBubble.py @@ -0,0 +1,133 @@ +# 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 + + 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(self._fill_color) + cr.set_source_rgb(*color) + cr.fill_preserve(); + + 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)) + + x = prog_x + y = prog_y + width = prog_width + height = prog_height + + 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(self._progress_color) + cr.set_source_rgb(*color) + cr.fill_preserve();