From 817a981288bc87f521447a13a22ee293a0dedf07 Mon Sep 17 00:00:00 2001 From: Simon Schampijer Date: Sun, 18 Jan 2009 16:30:53 +0100 Subject: [PATCH] Enhanced the 'Name this entry dialog' - created the canvastextview in sugar/graphics --- src/sugar/activity/Makefile.am | 3 +- src/sugar/activity/activity.py | 62 +----- src/sugar/activity/namingalert.py | 312 +++++++++++++++++++++++++++ src/sugar/graphics/Makefile.am | 1 + src/sugar/graphics/canvastextview.py | 39 ++++ 5 files changed, 359 insertions(+), 58 deletions(-) create mode 100644 src/sugar/activity/namingalert.py create mode 100644 src/sugar/graphics/canvastextview.py diff --git a/src/sugar/activity/Makefile.am b/src/sugar/activity/Makefile.am index ca1fa613..91f6ea8f 100644 --- a/src/sugar/activity/Makefile.am +++ b/src/sugar/activity/Makefile.am @@ -6,4 +6,5 @@ sugar_PYTHON = \ activityhandle.py \ activityservice.py \ bundlebuilder.py \ - main.py + main.py \ + namingalert.py \ No newline at end of file diff --git a/src/sugar/activity/activity.py b/src/sugar/activity/activity.py index f3ee5b84..d2ba2786 100644 --- a/src/sugar/activity/activity.py +++ b/src/sugar/activity/activity.py @@ -30,7 +30,7 @@ will need for a real activity. STABLE. """ # Copyright (C) 2006-2007 Red Hat, Inc. -# Copyright (C) 2007-2008 One Laptop Per Child +# Copyright (C) 2007-2009 One Laptop Per Child # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -55,7 +55,8 @@ from hashlib import sha1 import traceback import gconf -import gtk, gobject +import gtk +import gobject import dbus import dbus.service import cjson @@ -63,6 +64,7 @@ import cjson from sugar import util from sugar.presence import presenceservice from sugar.activity.activityservice import ActivityService +from sugar.activity.namingalert import NamingAlert from sugar.graphics import style from sugar.graphics.window import Window from sugar.graphics.toolbox import Toolbox @@ -74,7 +76,6 @@ from sugar.graphics.xocolor import XoColor from sugar.datastore import datastore from sugar.session import XSMPClient from sugar import wm -from sugar import _sugarext _ = lambda msg: gettext.dgettext('sugar-toolkit', msg) @@ -353,59 +354,6 @@ class _ActivitySession(gobject.GObject): def __sm_quit_cb(self, client): self.emit('quit') -class TitleAlert(gtk.Window): - __gtype_name__ = 'SugarTitleAlert' - - _BACKGROUND_COLOR = style.COLOR_BLACK.get_gdk_color() - - def __init__(self, activity): - gtk.Window.__init__(self) - - self.set_border_width(style.LINE_WIDTH) - offset = style.GRID_CELL_SIZE - width = gtk.gdk.screen_width() - offset * 2 - height = gtk.gdk.screen_height() - offset * 2 - self.set_size_request(width, height) - self.set_position(gtk.WIN_POS_CENTER_ALWAYS) - self.set_decorated(False) - self.set_resizable(False) - self.set_modal(True) - self.connect('realize', self.__realize_cb) - - self._activity = activity - - alignment = gtk.Alignment(xalign=0.5, yalign=0.5) - self.add(alignment) - alignment.show() - - vbox = gtk.VBox() - vbox.set_spacing(style.DEFAULT_PADDING) - alignment.add(vbox) - vbox.show() - - self._entry = gtk.Entry() - self._entry.props.text=self._activity.metadata['title'] - self._entry.modify_bg(gtk.STATE_INSENSITIVE, self._BACKGROUND_COLOR) - self._entry.modify_base(gtk.STATE_INSENSITIVE, self._BACKGROUND_COLOR) - vbox.pack_start(self._entry) - self._entry.show() - self._entry.connect('activate', self.__activate_cb) - - button = gtk.Button(_('Keep')) - vbox.pack_start(button) - button.show() - button.connect('clicked', self.__activate_cb) - - def __realize_cb(self, widget): - self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) - self.window.set_accept_focus(True) - self.modify_bg(gtk.STATE_NORMAL, self._BACKGROUND_COLOR) - - def __activate_cb(self, widget): - self._activity.metadata['title'] = self._entry.props.text - self._activity.metadata['title_set_by_user'] = '1' - self._activity.close() - class Activity(Window, gtk.Container): """This is the base Activity class that all other Activities derive from. This is where your activity starts. @@ -1027,7 +975,7 @@ class Activity(Window, gtk.Container): if not self._updating_jobject: self._complete_close() else: - title_alert = TitleAlert(self) + title_alert = NamingAlert(self, get_bundle_path()) title_alert.set_transient_for(self.get_toplevel()) title_alert.show() diff --git a/src/sugar/activity/namingalert.py b/src/sugar/activity/namingalert.py new file mode 100644 index 00000000..84705424 --- /dev/null +++ b/src/sugar/activity/namingalert.py @@ -0,0 +1,312 @@ +# Copyright (C) 2009 One Laptop Per Child +# +# 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. + +import gettext + +import gtk +import gobject +import hippo +import gconf + +from sugar.graphics import style +from sugar.graphics.icon import Icon +from sugar.graphics.xocolor import XoColor +from sugar.graphics.icon import CanvasIcon +from sugar.graphics.entry import CanvasEntry +from sugar.graphics.toolbutton import ToolButton +from sugar.graphics.canvastextview import CanvasTextView + +from sugar.bundle.activitybundle import ActivityBundle + +_ = lambda msg: gettext.dgettext('sugar-toolkit', msg) + +class NamingToolbar(gtk.Toolbar): + """ Toolbar of the naming alert + """ + __gtype_name__ = 'SugarNamingToolbar' + + __gsignals__ = { + 'keep-clicked': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + ([])) + } + def __init__(self): + gtk.Toolbar.__init__(self) + + client = gconf.client_get_default() + color = XoColor(client.get_string('/desktop/sugar/user/color')) + icon = Icon() + icon.set_from_icon_name('activity-journal', + gtk.ICON_SIZE_LARGE_TOOLBAR) + icon.props.xo_color = color + self._add_widget(icon) + + self._add_separator() + + self._title = gtk.Label(_('Name this entry')) + self._add_widget(self._title) + + self._add_separator(True) + + self._keep_button = ToolButton('dialog-ok') + self._keep_button.set_tooltip(_('Keep')) + self._keep_button.connect('clicked', self.__keep_button_clicked_cb) + self.insert(self._keep_button, -1) + self._keep_button.show() + + def _add_separator(self, expand=False): + separator = gtk.SeparatorToolItem() + separator.props.draw = False + if expand: + separator.set_expand(True) + else: + separator.set_size_request(style.DEFAULT_SPACING, -1) + self.insert(separator, -1) + separator.show() + + def _add_widget(self, widget, expand=False): + tool_item = gtk.ToolItem() + tool_item.set_expand(expand) + + tool_item.add(widget) + widget.show() + + self.insert(tool_item, -1) + tool_item.show() + + def __keep_button_clicked_cb(self, widget, data=None): + self.emit('keep-clicked') + +class FavoriteIcon(CanvasIcon): + def __init__(self, favorite): + CanvasIcon.__init__(self, icon_name='emblem-favorite', + box_width=style.GRID_CELL_SIZE * 3 / 5, + size=style.SMALL_ICON_SIZE) + self._favorite = None + self.set_favorite(favorite) + self.connect('button-release-event', self.__release_event_cb) + self.connect('motion-notify-event', self.__motion_notify_event_cb) + + def set_favorite(self, favorite): + if favorite == self._favorite: + return + + self._favorite = favorite + if favorite: + client = gconf.client_get_default() + color = XoColor(client.get_string('/desktop/sugar/user/color')) + self.props.xo_color = color + else: + self.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg() + self.props.fill_color = style.COLOR_WHITE.get_svg() + + def get_favorite(self): + return self._favorite + + favorite = gobject.property( + type=bool, default=False, getter=get_favorite, setter=set_favorite) + + def __release_event_cb(self, icon, event): + self.props.favorite = not self.props.favorite + + def __motion_notify_event_cb(self, icon, event): + if not self._favorite: + if event.detail == hippo.MOTION_DETAIL_ENTER: + icon.props.fill_color = style.COLOR_BUTTON_GREY.get_svg() + elif event.detail == hippo.MOTION_DETAIL_LEAVE: + icon.props.fill_color = style.COLOR_TRANSPARENT.get_svg() + +class NamingAlert(gtk.Window): + __gtype_name__ = 'SugarNamingAlert' + + def __init__(self, activity, bundle_path): + gtk.Window.__init__(self) + + self._bundle_path = bundle_path + self._favorite_icon = None + self._title = None + self._description = None + self._tags = None + + self.set_border_width(style.LINE_WIDTH) + offset = style.GRID_CELL_SIZE + width = gtk.gdk.screen_width() - offset * 2 + height = gtk.gdk.screen_height() - offset * 2 + self.set_size_request(width, height) + self.set_position(gtk.WIN_POS_CENTER_ALWAYS) + self.set_decorated(False) + self.set_resizable(False) + self.set_modal(True) + self.connect('realize', self.__realize_cb) + + self._activity = activity + + vbox = gtk.VBox() + self.add(vbox) + vbox.show() + + toolbar = NamingToolbar() + toolbar.connect('keep-clicked', self.__keep_cb) + vbox.pack_start(toolbar, False) + toolbar.show() + + canvas = hippo.Canvas() + self._root = hippo.CanvasBox() + self._root.props.background_color = style.COLOR_WHITE.get_int() + canvas.set_root(self._root) + vbox.pack_start(canvas) + canvas.show() + + body = self._create_body() + self._root.append(body, hippo.PACK_EXPAND) + + def _create_body(self): + body = hippo.CanvasBox() + body.props.orientation = hippo.ORIENTATION_VERTICAL + body.props.background_color = style.COLOR_WHITE.get_int() + body.props.padding_top = style.DEFAULT_SPACING * 3 + + header = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL, + padding=style.DEFAULT_PADDING, + padding_right=style.GRID_CELL_SIZE, + spacing=style.DEFAULT_SPACING) + body.append(header) + + descriptions = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL, + spacing=style.DEFAULT_SPACING * 3, + padding_left=style.GRID_CELL_SIZE, + padding_right=style.GRID_CELL_SIZE, + padding_top=style.DEFAULT_SPACING * 3) + + body.append(descriptions, hippo.PACK_EXPAND) + + first_column = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL, + spacing=style.DEFAULT_SPACING) + descriptions.append(first_column) + + second_column = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL, + spacing=style.DEFAULT_SPACING) + descriptions.append(second_column, hippo.PACK_EXPAND) + + self._favorite_icon = self._create_favorite_icon() + header.append(self._favorite_icon) + + activity_icon = self._create_activity_icon() + header.append(activity_icon) + + self._title = self._create_title() + header.append(self._title, hippo.PACK_EXPAND) + + if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL: + header.reverse() + + description_box, self._description = self._create_description() + second_column.append(description_box) + + tags_box, self._tags = self._create_tags() + second_column.append(tags_box) + + return body + + def _create_favorite_icon(self): + favorite_icon = FavoriteIcon(False) + return favorite_icon + + def _create_activity_icon(self): + activity_bundle = ActivityBundle(self._bundle_path) + activity_icon = CanvasIcon(file_name=activity_bundle.get_icon()) + if self._activity.metadata.has_key('icon-color') and \ + self._activity.metadata['icon-color']: + activity_icon.props.xo_color = XoColor( \ + self._activity.metadata['icon-color']) + return activity_icon + + def _create_title(self): + title = CanvasEntry() + title.set_background(style.COLOR_WHITE.get_html()) + title.props.text = self._activity.metadata.get('title', _('Untitled')) + return title + + def _create_description(self): + vbox = hippo.CanvasBox() + vbox.props.spacing = style.DEFAULT_SPACING + + text = hippo.CanvasText(text=_('Description:'), + font_desc=style.FONT_NORMAL.get_pango_desc()) + text.props.color = style.COLOR_BUTTON_GREY.get_int() + + if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL: + text.props.xalign = hippo.ALIGNMENT_END + else: + text.props.xalign = hippo.ALIGNMENT_START + + vbox.append(text) + + description = self._activity.metadata.get('description', '') + text_view = CanvasTextView(description, + box_height=style.GRID_CELL_SIZE * 2) + vbox.append(text_view, hippo.PACK_EXPAND) + + text_view.text_view_widget.props.accepts_tab = False + + return vbox, text_view + + def _create_tags(self): + vbox = hippo.CanvasBox() + vbox.props.spacing = style.DEFAULT_SPACING + + text = hippo.CanvasText(text=_('Tags:'), + font_desc=style.FONT_NORMAL.get_pango_desc()) + text.props.color = style.COLOR_BUTTON_GREY.get_int() + + if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL: + text.props.xalign = hippo.ALIGNMENT_END + else: + text.props.xalign = hippo.ALIGNMENT_START + + vbox.append(text) + + tags = self._activity.metadata.get('tags', '') + text_view = CanvasTextView(tags, box_height=style.GRID_CELL_SIZE * 2) + vbox.append(text_view, hippo.PACK_EXPAND) + + text_view.text_view_widget.props.accepts_tab = False + + return vbox, text_view + + def __realize_cb(self, widget): + self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + self.window.set_accept_focus(True) + + def __keep_cb(self, widget): + is_favorite = self._favorite_icon.get_favorite() + if is_favorite: + self._activity.metadata['keep'] = 1 + else: + self._activity.metadata['keep'] = 0 + + self._activity.metadata['title'] = self._title.props.text + + new_tags = self._tags.text_view_widget.props.buffer.props.text + self._activity.metadata['tags'] = new_tags + + new_description = \ + self._description.text_view_widget.props.buffer.props.text + self._activity.metadata['description'] = new_description + + self._activity.metadata['title_set_by_user'] = '1' + self._activity.close() diff --git a/src/sugar/graphics/Makefile.am b/src/sugar/graphics/Makefile.am index 3f147b4d..c4d5e61a 100644 --- a/src/sugar/graphics/Makefile.am +++ b/src/sugar/graphics/Makefile.am @@ -3,6 +3,7 @@ sugar_PYTHON = \ __init__.py \ alert.py \ animator.py \ + canvastextview.py \ combobox.py \ colorbutton.py \ entry.py \ diff --git a/src/sugar/graphics/canvastextview.py b/src/sugar/graphics/canvastextview.py new file mode 100644 index 00000000..481248db --- /dev/null +++ b/src/sugar/graphics/canvastextview.py @@ -0,0 +1,39 @@ +# Copyright (C) 2008 One Laptop Per Child +# +# 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. + +import gtk +import hippo + +from sugar.graphics import style + +class CanvasTextView(hippo.CanvasWidget): + def __init__(self, text, **kwargs): + hippo.CanvasWidget.__init__(self, **kwargs) + self.text_view_widget = gtk.TextView() + self.text_view_widget.props.buffer.props.text = text + self.text_view_widget.props.left_margin = style.DEFAULT_SPACING + self.text_view_widget.props.right_margin = style.DEFAULT_SPACING + self.text_view_widget.props.wrap_mode = gtk.WRAP_WORD + self.text_view_widget.show() + + # TODO: These fields should expand vertically instead of scrolling + scrolled_window = gtk.ScrolledWindow() + scrolled_window.set_shadow_type(gtk.SHADOW_OUT) + scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + scrolled_window.add(self.text_view_widget) + + self.props.widget = scrolled_window