[Sugar-devel] [PATCH sugar] Add copy-to option in the Journal

Simon Schampijer simon at schampijer.de
Tue May 24 18:53:36 EDT 2011


This will be part of replacing the keep button. The discussion is taking place
at: http://lists.sugarlabs.org/archive/sugar-devel/2011-May/031316.html
There are mockups and the implementation details.

Signed-off-by: Simon Schampijer <simon at laptop.org>
---
 src/jarabe/journal/journalactivity.py |    1 +
 src/jarabe/journal/journaltoolbox.py  |   86 +++++++-----------
 src/jarabe/journal/listview.py        |   13 +++
 src/jarabe/journal/model.py           |    2 +
 src/jarabe/journal/palettes.py        |  155 +++++++++++++++++++++++++++++----
 5 files changed, 185 insertions(+), 72 deletions(-)

diff --git a/src/jarabe/journal/journalactivity.py b/src/jarabe/journal/journalactivity.py
index a33038a..bb1c7f6 100644
--- a/src/jarabe/journal/journalactivity.py
+++ b/src/jarabe/journal/journalactivity.py
@@ -171,6 +171,7 @@ class JournalActivity(JournalWindow):
         self._list_view = ListView()
         self._list_view.connect('detail-clicked', self.__detail_clicked_cb)
         self._list_view.connect('clear-clicked', self.__clear_clicked_cb)
+        self._list_view.connect('volume-error', self.__volume_error_cb)
         self._main_view.pack_start(self._list_view)
         self._list_view.show()
 
diff --git a/src/jarabe/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py
index d825bc9..18d8cdf 100644
--- a/src/jarabe/journal/journaltoolbox.py
+++ b/src/jarabe/journal/journaltoolbox.py
@@ -26,6 +26,7 @@ import gobject
 import gio
 import gtk
 
+from sugar.graphics.palette import Palette
 from sugar.graphics.toolbox import Toolbox
 from sugar.graphics.toolcombobox import ToolComboBox
 from sugar.graphics.toolbutton import ToolButton
@@ -37,11 +38,12 @@ from sugar.graphics.xocolor import XoColor
 from sugar.graphics import iconentry
 from sugar.graphics import style
 from sugar import mime
-from sugar import profile
 
 from jarabe.model import bundleregistry
 from jarabe.journal import misc
 from jarabe.journal import model
+from jarabe.journal.palettes import ClipboardMenu
+from jarabe.journal.palettes import VolumeMenu
 
 
 _AUTOSEARCH_TIMEOUT = 1000
@@ -378,7 +380,7 @@ class EntryToolbar(gtk.Toolbar):
         self._copy.set_icon_widget(icon)
         icon.show()
 
-        self._copy.set_tooltip(_('Copy'))
+        self._copy.set_tooltip(_('Copy to'))
         self._copy.connect('clicked', self._copy_clicked_cb)
         self.add(self._copy)
         self._copy.show()
@@ -402,19 +404,7 @@ class EntryToolbar(gtk.Toolbar):
         misc.resume(self._metadata)
 
     def _copy_clicked_cb(self, button):
-        clipboard = gtk.Clipboard()
-        clipboard.set_with_data([('text/uri-list', 0, 0)],
-                                self.__clipboard_get_func_cb,
-                                self.__clipboard_clear_func_cb)
-
-    def __clipboard_get_func_cb(self, clipboard, selection_data, info, data):
-        # Get hold of a reference so the temp file doesn't get deleted
-        self._temp_file_path = model.get_file(self._metadata['uid'])
-        selection_data.set_uris(['file://' + self._temp_file_path])
-
-    def __clipboard_clear_func_cb(self, clipboard, data):
-        # Release and delete the temp file
-        self._temp_file_path = None
+        button.palette.popup(immediate=True, state=Palette.SECONDARY)
 
     def _erase_button_clicked_cb(self, button):
         registry = bundleregistry.get_registry()
@@ -427,24 +417,6 @@ class EntryToolbar(gtk.Toolbar):
     def _resume_menu_item_activate_cb(self, menu_item, service_name):
         misc.resume(self._metadata, service_name)
 
-    def _copy_menu_item_activate_cb(self, menu_item, mount_point):
-        file_path = model.get_file(self._metadata['uid'])
-
-        if not file_path or not os.path.exists(file_path):
-            logging.warn('Entries without a file cannot be copied.')
-            self.emit('volume-error',
-                      _('Entries without a file cannot be copied.'),
-                      _('Warning'))
-            return
-
-        try:
-            model.copy(self._metadata, mount_point)
-        except IOError, e:
-            logging.exception('Error while copying the entry. %s', e.strerror)
-            self.emit('volume-error',
-                      _('Error while copying the entry. %s') % e.strerror,
-                      _('Error'))
-
     def _refresh_copy_palette(self):
         palette = self._copy.get_palette()
 
@@ -452,35 +424,43 @@ class EntryToolbar(gtk.Toolbar):
             palette.menu.remove(menu_item)
             menu_item.destroy()
 
-        if self._metadata['mountpoint'] != '/':
-            journal_item = MenuItem(_('Journal'))
-            journal_item.set_image(Icon(
-                    icon_name='activity-journal',
-                    xo_color=profile.get_color(),
-                    icon_size=gtk.ICON_SIZE_MENU))
-            journal_item.connect('activate',
-                    self._copy_menu_item_activate_cb, '/')
-            journal_item.show()
-            palette.menu.append(journal_item)
+        client = gconf.client_get_default()
+        color = XoColor(client.get_string('/desktop/sugar/user/color'))
+
+        clipboard_menu = ClipboardMenu(self._metadata)
+        clipboard_menu.set_image(Icon(icon_name='transfer-from',
+                                      xo_color=color,
+                                      icon_size=gtk.ICON_SIZE_MENU))
+        clipboard_menu.connect('volume-error', self.__volume_error_cb)
+        palette.menu.append(clipboard_menu)
+        clipboard_menu.show()
+
+        journal_menu = VolumeMenu(self._metadata, _('Journal'), '/')
+        journal_menu.set_image(Icon(icon_name='activity-journal',
+                                    xo_color=color,
+                                    icon_size=gtk.ICON_SIZE_MENU))
+        journal_menu.connect('volume-error', self.__volume_error_cb)
+        palette.menu.append(journal_menu)
+        journal_menu.show()
 
         volume_monitor = gio.volume_monitor_get()
+        icon_theme = gtk.icon_theme_get_default()
         for mount in volume_monitor.get_mounts():
             if self._metadata['mountpoint'] == mount.get_root().get_path():
                 continue
-            menu_item = MenuItem(mount.get_name())
-
-            icon_theme = gtk.icon_theme_get_default()
+            volume_menu = VolumeMenu(self._metadata, mount.get_name(),
+                                     mount.get_root().get_path())
             for name in mount.get_icon().props.names:
                 if icon_theme.has_icon(name):
-                    menu_item.set_image(Icon(icon_name=name,
-                                             icon_size=gtk.ICON_SIZE_MENU))
+                    volume_menu.set_image(Icon(icon_name=name,
+                                               icon_size=gtk.ICON_SIZE_MENU))
                     break
+            volume_menu.connect('volume-error', self.__volume_error_cb)
+            palette.menu.append(volume_menu)
+            volume_menu.show()
 
-            menu_item.connect('activate',
-                              self._copy_menu_item_activate_cb,
-                              mount.get_root().get_path())
-            palette.menu.append(menu_item)
-            menu_item.show()
+    def __volume_error_cb(self, menu_item, message, severity):
+        self.emit('volume-error', message, severity)
 
     def _refresh_resume_palette(self):
         if self._metadata.get('activity_id', ''):
diff --git a/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py
index 0aee1b7..f779fc4 100644
--- a/src/jarabe/journal/listview.py
+++ b/src/jarabe/journal/listview.py
@@ -469,6 +469,8 @@ class ListView(BaseListView):
     __gsignals__ = {
         'detail-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                            ([object])),
+        'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+                         ([str, str])),
     }
 
     def __init__(self):
@@ -484,6 +486,7 @@ class ListView(BaseListView):
 
         self.cell_icon.connect('clicked', self.__icon_clicked_cb)
         self.cell_icon.connect('detail-clicked', self.__detail_clicked_cb)
+        self.cell_icon.connect('volume-error', self.__volume_error_cb)
 
         cell_detail = CellRendererDetail(self.tree_view)
         cell_detail.connect('clicked', self.__detail_cell_clicked_cb)
@@ -525,6 +528,9 @@ class ListView(BaseListView):
     def __detail_clicked_cb(self, cell, uid):
         self.emit('detail-clicked', uid)
 
+    def __volume_error_cb(self, cell, message, severity):
+        self.emit('volume-error', message, severity)
+
     def __icon_clicked_cb(self, cell, path):
         row = self.tree_view.get_model()[path]
         metadata = model.get(row[ListModel.COLUMN_UID])
@@ -579,6 +585,8 @@ class CellRendererActivityIcon(CellRendererIcon):
     __gsignals__ = {
         'detail-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                            ([str])),
+        'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+                           ([str, str])),
     }
 
     def __init__(self, tree_view):
@@ -603,11 +611,16 @@ class CellRendererActivityIcon(CellRendererIcon):
         palette = ObjectPalette(metadata, detail=True)
         palette.connect('detail-clicked',
                         self.__detail_clicked_cb)
+        palette.connect('volume-error',
+                        self.__volume_error_cb)
         return palette
 
     def __detail_clicked_cb(self, palette, uid):
         self.emit('detail-clicked', uid)
 
+    def __volume_error_cb(self, palette, message, severity):
+        self.emit('volume-error', message, severity)
+
     def set_show_palette(self, show_palette):
         self._show_palette = show_palette
 
diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py
index fd25681..1891c84 100644
--- a/src/jarabe/journal/model.py
+++ b/src/jarabe/journal/model.py
@@ -616,6 +616,8 @@ def copy(metadata, mount_point):
     """
     metadata = get(metadata['uid'])
     file_path = get_file(metadata['uid'])
+    if file_path is None:
+        file_path = ''
 
     metadata['mountpoint'] = mount_point
     del metadata['uid']
diff --git a/src/jarabe/journal/palettes.py b/src/jarabe/journal/palettes.py
index 9ae1afb..0b3f7d2 100644
--- a/src/jarabe/journal/palettes.py
+++ b/src/jarabe/journal/palettes.py
@@ -16,10 +16,12 @@
 
 from gettext import gettext as _
 import logging
+import os
 
 import gobject
 import gtk
 import gconf
+import gio
 
 from sugar.graphics import style
 from sugar.graphics.palette import Palette
@@ -42,12 +44,13 @@ class ObjectPalette(Palette):
     __gsignals__ = {
         'detail-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                            ([str])),
+        'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+                         ([str, str])),
     }
 
     def __init__(self, metadata, detail=False):
 
         self._metadata = metadata
-        self._temp_file_path = None
 
         activity_icon = Icon(icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
         activity_icon.props.file = misc.get_icon_name(metadata)
@@ -87,14 +90,17 @@ class ObjectPalette(Palette):
 
         client = gconf.client_get_default()
         color = XoColor(client.get_string('/desktop/sugar/user/color'))
-        menu_item = MenuItem(_('Copy'))
+        menu_item = MenuItem(_('Copy to'))
         icon = Icon(icon_name='edit-copy', xo_color=color,
                     icon_size=gtk.ICON_SIZE_MENU)
         menu_item.set_image(icon)
-        menu_item.connect('activate', self.__copy_activate_cb)
         self.menu.append(menu_item)
         menu_item.show()
 
+        copy_menu = CopyMenu(metadata)
+        copy_menu.connect('volume-error', self.__volume_error_cb)
+        menu_item.set_submenu(copy_menu)
+
         menu_item = MenuItem(_('Send to'), 'document-send')
         self.menu.append(menu_item)
         menu_item.show()
@@ -117,28 +123,15 @@ class ObjectPalette(Palette):
     def __start_activate_cb(self, menu_item):
         misc.resume(self._metadata)
 
-    def __copy_activate_cb(self, menu_item):
-        clipboard = gtk.Clipboard()
-        clipboard.set_with_data([('text/uri-list', 0, 0)],
-                                self.__clipboard_get_func_cb,
-                                self.__clipboard_clear_func_cb)
-
-    def __clipboard_get_func_cb(self, clipboard, selection_data, info, data):
-        # Get hold of a reference so the temp file doesn't get deleted
-        self._temp_file_path = model.get_file(self._metadata['uid'])
-        logging.debug('__clipboard_get_func_cb %r', self._temp_file_path)
-        selection_data.set_uris(['file://' + self._temp_file_path])
-
-    def __clipboard_clear_func_cb(self, clipboard, data):
-        # Release and delete the temp file
-        self._temp_file_path = None
-
     def __erase_activate_cb(self, menu_item):
         model.delete(self._metadata['uid'])
 
     def __detail_activate_cb(self, menu_item):
         self.emit('detail-clicked', self._metadata['uid'])
 
+    def __volume_error_cb(self, menu_item, message, severity):
+        self.emit('volume-error', message, severity)
+
     def __friend_selected_cb(self, menu_item, buddy):
         logging.debug('__friend_selected_cb')
         file_name = model.get_file(self._metadata['uid'])
@@ -154,6 +147,130 @@ class ObjectPalette(Palette):
                                     mime_type)
 
 
+class CopyMenu(gtk.Menu):
+    __gtype_name__ = 'JournalCopyMenu'
+
+    __gsignals__ = {
+        'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+                         ([str, str])),
+    }
+
+    def __init__(self, metadata):
+        gobject.GObject.__init__(self)
+
+        self._metadata = metadata
+
+        client = gconf.client_get_default()
+        color = XoColor(client.get_string('/desktop/sugar/user/color'))
+
+        clipboard_menu = ClipboardMenu(self._metadata)
+        clipboard_menu.set_image(Icon(icon_name='transfer-from',
+                                      xo_color=color,
+                                      icon_size=gtk.ICON_SIZE_MENU))
+        clipboard_menu.connect('volume-error', self.__volume_error_cb)
+        self.append(clipboard_menu)
+        clipboard_menu.show()
+
+        journal_menu = VolumeMenu(self._metadata, _('Journal'), '/')
+        journal_menu.set_image(Icon(icon_name='activity-journal',
+                                    xo_color=color,
+                                    icon_size=gtk.ICON_SIZE_MENU))
+        journal_menu.connect('volume-error', self.__volume_error_cb)
+        self.append(journal_menu)
+        journal_menu.show()
+
+        volume_monitor = gio.volume_monitor_get()
+        icon_theme = gtk.icon_theme_get_default()
+        for mount in volume_monitor.get_mounts():
+            if self._metadata['mountpoint'] == mount.get_root().get_path():
+                continue
+            volume_menu = VolumeMenu(self._metadata, mount.get_name(),
+                                   mount.get_root().get_path())
+            for name in mount.get_icon().props.names:
+                if icon_theme.has_icon(name):
+                    volume_menu.set_image(Icon(icon_name=name,
+                                               icon_size=gtk.ICON_SIZE_MENU))
+                    break
+            volume_menu.connect('volume-error', self.__volume_error_cb)
+            self.append(volume_menu)
+            volume_menu.show()
+
+    def __volume_error_cb(self, menu_item, message, severity):
+        self.emit('volume-error', message, severity)
+
+
+class VolumeMenu(MenuItem):
+    __gtype_name__ = 'JournalVolumeMenu'
+
+    __gsignals__ = {
+        'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+                         ([str, str])),
+    }
+
+    def __init__(self, metadata, label, mount_point):
+        MenuItem.__init__(self, label)
+        self._metadata = metadata
+        self.connect('activate', self.__copy_to_volume_cb, mount_point)
+
+    def __copy_to_volume_cb(self, menu_item, mount_point):
+        file_path = model.get_file(self._metadata['uid'])
+        if mount_point != '/':
+            if not file_path or not os.path.exists(file_path):
+                logging.warn('Entries without a file cannot be copied.')
+                self.emit('volume-error',
+                          _('Entries without a file cannot be copied.'),
+                          _('Warning'))
+                return
+
+        try:
+            model.copy(self._metadata, mount_point)
+        except IOError, e:
+            logging.exception('Error while copying the entry. %s', e.strerror)
+            self.emit('volume-error',
+                      _('Error while copying the entry. %s') % e.strerror,
+                      _('Error'))
+
+
+class ClipboardMenu(MenuItem):
+    __gtype_name__ = 'JournalClipboardMenu'
+
+    __gsignals__ = {
+        'volume-error': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+                         ([str, str])),
+    }
+
+    def __init__(self, metadata):
+        MenuItem.__init__(self, _('Clipboard'))
+
+        self._temp_file_path = None
+        self._metadata = metadata
+        self.connect('activate', self.__copy_to_clipboard_cb)
+
+    def __copy_to_clipboard_cb(self, menu_item):
+        file_path = model.get_file(self._metadata['uid'])
+        if not file_path or not os.path.exists(file_path):
+            logging.warn('Entries without a file cannot be copied.')
+            self.emit('volume-error',
+                      _('Entries without a file cannot be copied.'),
+                      _('Warning'))
+            return
+
+        clipboard = gtk.Clipboard()
+        clipboard.set_with_data([('text/uri-list', 0, 0)],
+                                self.__clipboard_get_func_cb,
+                                self.__clipboard_clear_func_cb)
+
+    def __clipboard_get_func_cb(self, clipboard, selection_data, info, data):
+        # Get hold of a reference so the temp file doesn't get deleted
+        self._temp_file_path = model.get_file(self._metadata['uid'])
+        logging.debug('__clipboard_get_func_cb %r', self._temp_file_path)
+        selection_data.set_uris(['file://' + self._temp_file_path])
+
+    def __clipboard_clear_func_cb(self, clipboard, data):
+        # Release and delete the temp file
+        self._temp_file_path = None
+
+
 class FriendsMenu(gtk.Menu):
     __gtype_name__ = 'JournalFriendsMenu'
 
-- 
1.7.4



More information about the Sugar-devel mailing list